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
;
32 // ////////////////////////////////////////////////////////////////////////// //
33 public __gshared Color
[256] d2dpal
;
36 public void loadD2DPalette () {
39 auto fl
= openFile("playpal.pal");
40 fl
.rawReadExact(vgapal
[]);
41 foreach (ref ubyte b
; vgapal
) if (b
> 63) b
= 63; // just in case
43 foreach (immutable idx
; 0..256) {
44 d2dpal
[idx
].r
= cast(ubyte)(vgapal
[idx
*3+0]*255/63);
45 d2dpal
[idx
].g
= cast(ubyte)(vgapal
[idx
*3+1]*255/63);
46 d2dpal
[idx
].b
= cast(ubyte)(vgapal
[idx
*3+2]*255/63);
49 // color 0 is transparent
54 // ////////////////////////////////////////////////////////////////////////// //
55 public final class D2DImage
{
59 private TrueColorImage mimg
;
62 private this () pure nothrow @safe {}
65 import std
.path
: extension
;
66 auto fl
= openFile(name
);
67 conwriteln("loading image '", name
, "'");
71 this (int awdt
, int ahgt
) nothrow @trusted {
72 assert(awdt
>= 0 && ahgt
>= 0);
76 if (awdt
> 0 && ahgt
> 0) {
78 mimg
= new TrueColorImage(awdt
, ahgt
);
79 } catch (Exception e
) {
80 assert(0, e
.toString
);
82 mimg
.imageData
.bytes
[] = 0;
86 /// will not clone texture!
87 D2DImage
clone () const nothrow @trusted {
88 auto res
= new D2DImage();
92 res
.mheight
= mheight
;
95 res
.mimg
= new TrueColorImage(mwidth
, mheight
);
96 } catch (Exception e
) {
97 assert(0, e
.toString
);
99 res
.mimg
.imageData
.bytes
[] = mimg
.imageData
.bytes
[];
104 void resize (int awdt
, int ahgt
) nothrow @trusted {
105 assert(awdt
>= 0 && ahgt
>= 0);
106 if (mwidth
!= awdt || mheight
!= ahgt
) {
111 if (awdt
> 0 && ahgt
> 0) {
113 mimg
= new TrueColorImage(awdt
, ahgt
);
114 } catch (Exception e
) {
115 assert(0, e
.toString
);
117 mimg
.imageData
.bytes
[] = 0;
120 if (mwidth
<= 0 || mheight
<= 0) mimg
= null;
124 //if (mtex !is null) mtex.clear;
125 //if (mimg !is null) mimg.clear;
128 mwidth
= mheight
= 0;
132 void removeOffset () { sx
= sy
= 0; }
134 @property const pure nothrow @safe @nogc {
135 bool valid () { pragma(inline
, true); return (mimg
!is null && mwidth
> 0 && mheight
> 0); }
136 int width () { pragma(inline
, true); return mwidth
; }
137 int height () { pragma(inline
, true); return mheight
; }
140 @property TrueColorImage
img () pure nothrow @safe @nogc { pragma(inline
, true); return mimg
; }
142 Color
opIndex (usize y
, usize x
) const /*pure*/ nothrow @safe @nogc { pragma(inline
, true); return getPixel(x
, y
); }
143 void opIndex (Color clr
, usize y
, usize x
) nothrow @safe @nogc { pragma(inline
, true); setPixel(x
, y
, clr
); }
144 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
); }
146 Color
getPixel (int x
, int y
) const /*pure*/ nothrow @trusted @nogc {
147 pragma(inline
, true);
148 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));
151 void setPixel (int x
, int y
, Color clr
) nothrow @trusted @nogc {
152 pragma(inline
, true);
153 if (mimg
!is null && x
>= 0 && y
>= 0 && x
< mwidth
&& y
< mheight
) (cast(Color
*)mimg
.imageData
.bytes
.ptr
)[y
*mwidth
+x
] = clr
;
156 void putPixel (int x
, int y
, Color clr
) nothrow @trusted @nogc {
157 pragma(inline
, true);
158 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
;
161 void toBlackAndWhite () {
163 foreach (int y
; 0..mheight
) {
164 foreach (int x
; 0..mwidth
) {
165 Color clr
= getPixel(x
, y
);
166 int i
= cast(int)(0.2126*clr
.r
+0.7152*clr
.g
+0.0722*clr
.b
);
167 if (i
> 255) i
= 255; // just in case
168 setPixel(x
, y
, Color(i
&0xff, i
&0xff, i
&0xff, clr
.a
));
173 void toBlackAndWhiteChan(string chan
) () {
174 static assert(chan
== "red" || chan
== "r" || chan
== "green" || chan
== "g" || chan
== "blue" || chan
== "b", "invalid channel");
176 foreach (int y
; 0..mheight
) {
177 foreach (int x
; 0..mwidth
) {
178 Color clr
= getPixel(x
, y
);
179 static if (chan
== "red" || chan
== "r") ubyte i
= clr
.r
;
180 else static if (chan
== "green" || chan
== "g") ubyte i
= clr
.g
;
181 else static if (chan
== "blue" || chan
== "b") ubyte i
= clr
.b
;
182 else static assert(0, "wtf?!");
183 setPixel(x
, y
, Color(i
, i
, i
, clr
.a
));
188 D2DImage
blackAndWhite () {
189 auto res
= this.clone();
194 D2DImage
blackAndWhiteChan(string chan
) () {
195 auto res
= this.clone();
196 res
.toBlackAndWhiteChan
!chan
;
200 /// mirror image horizontally
203 foreach (int y
; 0..height
) {
204 foreach (int x
; 0..width
/2) {
206 auto c0
= getPixel(x
, y
);
207 auto c1
= getPixel(mx
, y
);
216 if (mtex
is null && valid
) mtex
= new Texture(mimg
, Texture
.Option
.Nearest
);
221 if (mtex
is null) mtex
= new Texture(mimg
, Texture
.Option
.Nearest
); else mtex
.setFromImage(mimg
);
226 if (!valid
) return 0;
227 if (mtex
is null) createTex();
228 return (mtex
!is null ? mtex
.id
: 0);
231 void savePng (string fname
) {
232 if (!valid
) throw new Exception("can't save empty image");
233 auto png
= pngFromImage(mimg
);
240 auto chk
= Chunk
.create("dtDi", (cast(ubyte*)&di)[0..di.sizeof
]);
244 if (sx
>= short.min
&& sx
<= short.max
&& sy
>= short.min
&& sy
<= short.max
) {
246 di.sx
= cast(short)sx
;
247 di.sy
= cast(short)sy
;
249 auto chk
= Chunk
.create("grAb", (cast(ubyte*)&di)[0..di.sizeof
]);
252 auto fo
= vfsDiskOpen(fname
, "w");
253 fo
.rawWriteExact(writePng(png
));
257 static align(1) struct D2DInfoChunk
{
259 ubyte ver
; // version; 0 for now; versions should be compatible
262 void fixEndian () nothrow @trusted @nogc {
264 import std
.bitmanip
: swapEndian
;
271 static align(1) struct GrabChunk
{
275 void fixEndian () nothrow @trusted @nogc {
276 version(LittleEndian
) {
277 import std
.bitmanip
: swapEndian
;
284 void loadPng (VFile fl
) {
285 auto flsize
= fl
.size
-fl
.tell
;
286 if (flsize
< 8 || flsize
> 1024*1024*32) throw new Exception("png image too big");
287 auto data
= new ubyte[](cast(uint)flsize
);
288 fl
.rawReadExact(data
);
289 auto png
= readPng(data
);
290 auto ximg
= imageFromPng(png
).getAsTrueColorImage
;
291 if (ximg
is null) throw new Exception("png: wtf?!");
292 if (ximg
.width
< 1 || ximg
.height
< 1) throw new Exception("png image too small");
294 mheight
= ximg
.height
;
297 foreach (ref chk
; png
.chunks
) {
298 if (chk
.type
[] == "dtDi") {
300 if (chk
.size
>= D2DInfoChunk
.sizeof
) {
301 auto di = *cast(D2DInfoChunk
*)chk
.payload
.ptr
;
305 //conwriteln("found 'dtDi' chunk! sx=", di.sx, "; sy=", di.sy);
307 } else if (chk
.type
[] == "grAb") {
309 if (chk
.size
>= GrabChunk
.sizeof
) {
310 auto di = *cast(GrabChunk
*)chk
.payload
.ptr
;
314 //conwriteln("found 'dtDi' chunk! sx=", di.sx, "; sy=", di.sy);
320 void loadVga (VFile fl
) {
321 //conwriteln(" loading .VGA image");
322 auto w
= fl
.readNum
!ushort();
323 auto h
= fl
.readNum
!ushort();
324 auto isx
= fl
.readNum
!short();
325 auto isy
= fl
.readNum
!short();
326 //conwriteln(" loading .VGA image; w=", w, "; h=", h, "; isx=", isx, "; isy=", isy);
327 if (w
< 1 || w
> 32760) throw new Exception("invalid vga image width");
328 if (h
< 1 || h
> 32760) throw new Exception("invalid vga image height");
329 auto data
= new ubyte[](w
*h
);
330 fl
.rawReadExact(data
[]);
332 assert(mimg
!is null);
333 assert(mimg
.width
== w
);
334 assert(mimg
.height
== h
);
336 assert(mheight
== h
);
337 //conwriteln(" !!!");
340 foreach (int y
; 0..height
) {
341 foreach (int x
; 0..width
) {
342 setPixel(x
, y
, d2dpal
.ptr
[data
.ptr
[y
*w
+x
]]);
347 void loadJpeg (VFile fl
) {
348 auto jpg
= new JpegDecoder(fl
, JpegDecoder
.Upsampling
.BILINEAR
);
349 if (jpg
.image
.width
< 1 || jpg
.image
.width
> 32760) throw new Exception("invalid image width");
350 if (jpg
.image
.height
< 1 || jpg
.image
.height
> 32760) throw new Exception("invalid image height");
352 mwidth
= jpg
.image
.width
;
353 mheight
= jpg
.image
.height
;
358 void load (VFile fl
) {
359 scope(failure
) fl
.seek(0);
362 fl
.rawReadExact(sign
[]);
365 if (sign
== "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") {
370 if (sign
[0..2] == "\xff\xd8") {
371 fl
.seek(-2, Seek
.End
);
372 fl
.rawReadExact(sign
[0..2]);
374 if (sign
[0..2] == "\xff\xd9") {
379 // alas, this must be vga