1 /* DooM2D: Midnight on the Firing Line
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module d2dimage
is aliced
;
31 // ////////////////////////////////////////////////////////////////////////// //
32 public __gshared Color
[256] d2dpal
;
35 public void loadD2DPalette () {
38 auto fl
= openFile("playpal.pal");
39 fl
.rawReadExact(vgapal
[]);
40 foreach (ref ubyte b
; vgapal
) if (b
> 63) b
= 63; // just in case
42 foreach (immutable idx
; 0..256) {
43 d2dpal
[idx
].r
= cast(ubyte)(vgapal
[idx
*3+0]*255/63);
44 d2dpal
[idx
].g
= cast(ubyte)(vgapal
[idx
*3+1]*255/63);
45 d2dpal
[idx
].b
= cast(ubyte)(vgapal
[idx
*3+2]*255/63);
48 // color 0 is transparent
53 // ////////////////////////////////////////////////////////////////////////// //
54 public final class D2DImage
{
58 private TrueColorImage mimg
;
61 private this () pure nothrow @safe {}
64 import std
.path
: extension
;
65 auto fl
= openFile(name
);
66 conwriteln("loading image '", name
, "'");
70 this (int awdt
, int ahgt
) nothrow @trusted {
71 assert(awdt
>= 0 && ahgt
>= 0);
75 if (awdt
> 0 && ahgt
> 0) {
77 mimg
= new TrueColorImage(awdt
, ahgt
);
78 } catch (Exception e
) {
79 assert(0, e
.toString
);
81 mimg
.imageData
.bytes
[] = 0;
85 /// will not clone texture!
86 D2DImage
clone () const nothrow @trusted {
87 auto res
= new D2DImage();
91 res
.mheight
= mheight
;
94 res
.mimg
= new TrueColorImage(mwidth
, mheight
);
95 } catch (Exception e
) {
96 assert(0, e
.toString
);
98 res
.mimg
.imageData
.bytes
[] = mimg
.imageData
.bytes
[];
103 void resize (int awdt
, int ahgt
) nothrow @trusted {
104 assert(awdt
>= 0 && ahgt
>= 0);
105 if (mwidth
!= awdt || mheight
!= ahgt
) {
110 if (awdt
> 0 && ahgt
> 0) {
112 mimg
= new TrueColorImage(awdt
, ahgt
);
113 } catch (Exception e
) {
114 assert(0, e
.toString
);
116 mimg
.imageData
.bytes
[] = 0;
119 if (mwidth
<= 0 || mheight
<= 0) mimg
= null;
123 //if (mtex !is null) mtex.clear;
124 //if (mimg !is null) mimg.clear;
127 mwidth
= mheight
= 0;
131 void removeOffset () { sx
= sy
= 0; }
133 @property const pure nothrow @safe @nogc {
134 bool valid () { pragma(inline
, true); return (mimg
!is null && mwidth
> 0 && mheight
> 0); }
135 int width () { pragma(inline
, true); return mwidth
; }
136 int height () { pragma(inline
, true); return mheight
; }
139 @property TrueColorImage
img () pure nothrow @safe @nogc { pragma(inline
, true); return mimg
; }
141 Color
opIndex (usize y
, usize x
) const /*pure*/ nothrow @safe @nogc { pragma(inline
, true); return getPixel(x
, y
); }
142 void opIndex (Color clr
, usize y
, usize x
) nothrow @safe @nogc { pragma(inline
, true); setPixel(x
, y
, clr
); }
143 void opIndex (Color clr
, usize y
, usize x
, bool ignoreTransparent
) nothrow @safe @nogc { pragma(inline
, true); if (!ignoreTransparent || clr
.a
!= 0) setPixel(x
, y
, clr
); }
145 Color
getPixel (int x
, int y
) const /*pure*/ nothrow @trusted @nogc {
146 pragma(inline
, true);
147 return (mimg
!is null && x
>= 0 && y
>= 0 && x
< mwidth
&& y
< mheight ?
(cast(const(Color
*))mimg
.imageData
.bytes
.ptr
)[y
*mwidth
+x
] : Color(0, 0, 0, 0));
150 void setPixel (int x
, int y
, Color clr
) nothrow @trusted @nogc {
151 pragma(inline
, true);
152 if (mimg
!is null && x
>= 0 && y
>= 0 && x
< mwidth
&& y
< mheight
) (cast(Color
*)mimg
.imageData
.bytes
.ptr
)[y
*mwidth
+x
] = clr
;
155 void putPixel (int x
, int y
, Color clr
) nothrow @trusted @nogc {
156 pragma(inline
, true);
157 if (mimg
!is null && x
>= 0 && y
>= 0 && x
< mwidth
&& y
< mheight
&& clr
.a
!= 0) (cast(Color
*)mimg
.imageData
.bytes
.ptr
)[y
*mwidth
+x
] = clr
;
160 void toBlackAndWhite () {
162 foreach (int y
; 0..mheight
) {
163 foreach (int x
; 0..mwidth
) {
164 Color clr
= getPixel(x
, y
);
165 int i
= cast(int)(0.2126*clr
.r
+0.7152*clr
.g
+0.0722*clr
.b
);
166 if (i
> 255) i
= 255; // just in case
167 setPixel(x
, y
, Color(i
&0xff, i
&0xff, i
&0xff, clr
.a
));
172 void toBlackAndWhiteChan(string chan
) () {
173 static assert(chan
== "red" || chan
== "r" || chan
== "green" || chan
== "g" || chan
== "blue" || chan
== "b", "invalid channel");
175 foreach (int y
; 0..mheight
) {
176 foreach (int x
; 0..mwidth
) {
177 Color clr
= getPixel(x
, y
);
178 static if (chan
== "red" || chan
== "r") ubyte i
= clr
.r
;
179 else static if (chan
== "green" || chan
== "g") ubyte i
= clr
.g
;
180 else static if (chan
== "blue" || chan
== "b") ubyte i
= clr
.b
;
181 else static assert(0, "wtf?!");
182 setPixel(x
, y
, Color(i
, i
, i
, clr
.a
));
187 D2DImage
blackAndWhite () {
188 auto res
= this.clone();
193 D2DImage
blackAndWhiteChan(string chan
) () {
194 auto res
= this.clone();
195 res
.toBlackAndWhiteChan
!chan
;
199 /// mirror image horizontally
202 foreach (int y
; 0..height
) {
203 foreach (int x
; 0..width
/2) {
205 auto c0
= getPixel(x
, y
);
206 auto c1
= getPixel(mx
, y
);
215 if (mtex
is null && valid
) mtex
= new Texture(mimg
, Texture
.Option
.Nearest
);
220 if (mtex
is null) mtex
= new Texture(mimg
, Texture
.Option
.Nearest
); else mtex
.setFromImage(mimg
);
225 if (!valid
) return 0;
226 if (mtex
is null) createTex();
227 return (mtex
!is null ? mtex
.id
: 0);
230 void savePng (string fname
) {
231 if (!valid
) throw new Exception("can't save empty image");
232 auto png
= pngFromImage(mimg
);
239 auto chk
= Chunk
.create("dtDi", (cast(ubyte*)&di)[0..di.sizeof
]);
243 if (sx
>= short.min
&& sx
<= short.max
&& sy
>= short.min
&& sy
<= short.max
) {
245 di.sx
= cast(short)sx
;
246 di.sy
= cast(short)sy
;
248 auto chk
= Chunk
.create("grAb", (cast(ubyte*)&di)[0..di.sizeof
]);
251 auto fo
= vfsDiskOpen(fname
, "w");
252 fo
.rawWriteExact(writePng(png
));
256 static align(1) struct D2DInfoChunk
{
258 ubyte ver
; // version; 0 for now; versions should be compatible
261 void fixEndian () nothrow @trusted @nogc {
263 import std
.bitmanip
: swapEndian
;
270 static align(1) struct GrabChunk
{
274 void fixEndian () nothrow @trusted @nogc {
275 version(LittleEndian
) {
276 import std
.bitmanip
: swapEndian
;
283 void loadPng (VFile fl
) {
284 auto flsize
= fl
.size
-fl
.tell
;
285 if (flsize
< 8 || flsize
> 1024*1024*32) throw new Exception("png image too big");
286 auto data
= new ubyte[](cast(uint)flsize
);
287 fl
.rawReadExact(data
);
288 auto png
= readPng(data
);
289 auto ximg
= imageFromPng(png
).getAsTrueColorImage
;
290 if (ximg
is null) throw new Exception("png: wtf?!");
291 if (ximg
.width
< 1 || ximg
.height
< 1) throw new Exception("png image too small");
293 mheight
= ximg
.height
;
296 foreach (ref chk
; png
.chunks
) {
297 if (chk
.type
[] == "dtDi") {
299 if (chk
.size
>= D2DInfoChunk
.sizeof
) {
300 auto di = *cast(D2DInfoChunk
*)chk
.payload
.ptr
;
304 //conwriteln("found 'dtDi' chunk! sx=", di.sx, "; sy=", di.sy);
306 } else if (chk
.type
[] == "grAb") {
308 if (chk
.size
>= GrabChunk
.sizeof
) {
309 auto di = *cast(GrabChunk
*)chk
.payload
.ptr
;
313 //conwriteln("found 'dtDi' chunk! sx=", di.sx, "; sy=", di.sy);
319 void loadVga (VFile fl
) {
320 //conwriteln(" loading .VGA image");
321 auto w
= fl
.readNum
!ushort();
322 auto h
= fl
.readNum
!ushort();
323 auto isx
= fl
.readNum
!short();
324 auto isy
= fl
.readNum
!short();
325 //conwriteln(" loading .VGA image; w=", w, "; h=", h, "; isx=", isx, "; isy=", isy);
326 if (w
< 1 || w
> 32760) throw new Exception("invalid vga image mwidth");
327 if (h
< 1 || h
> 32760) throw new Exception("invalid vga image mheight");
328 auto data
= new ubyte[](w
*h
);
329 fl
.rawReadExact(data
[]);
331 assert(mimg
!is null);
332 assert(mimg
.width
== w
);
333 assert(mimg
.height
== h
);
335 assert(mheight
== h
);
336 //conwriteln(" !!!");
339 foreach (int y
; 0..height
) {
340 foreach (int x
; 0..width
) {
341 setPixel(x
, y
, d2dpal
.ptr
[data
.ptr
[y
*w
+x
]]);
346 void load (VFile fl
) {
348 scope(failure
) fl
.seek(stp
);
350 fl
.rawReadExact(sign
[]);
352 if (sign
== "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") {