1 /* This PNG library is based on Adam Ruppe's PNG code, which he released into
2  * the public domain.  Basically, I adapted it by stripping out everything
3  * that I didn't need.  It's currently used for saving PNGs in the DFL port.
4  *
5  * This code is currently not considered part of the public API of plot2kill.
6  *
7  * License:
8  *
9  * Boost Software License - Version 1.0 - August 17th, 2003
10  *
11  * Permission is hereby granted, free of charge, to any person or organization
12  * obtaining a copy of the software and accompanying documentation covered by
13  * this license (the "Software") to use, reproduce, display, distribute,
14  * execute, and transmit the Software, and to prepare derivative works of the
15  * Software, and to permit third-parties to whom the Software is furnished to
16  * do so, all subject to the following:
17  *
18  * The copyright notices in the Software and this entire statement, including
19  * the above license grant, this restriction and the following disclaimer,
20  * must be included in all copies of the Software, in whole or in part, and
21  * all derivative works of the Software, unless such copies or derivative
22  * works are solely in the form of machine-executable object code generated by
23  * a source language processor.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
28  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
29  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
30  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31  * DEALINGS IN THE SOFTWARE.
32  */
33 module plot2kill.png;
34 
35 version(dfl) {
36 
37 package:
38 
39 import plot2kill.util;
40 import core.stdc.stdlib : malloc, free;
41 
42 // This function is by David Simcha 2010.
43 
44 // Save an array of pixels in backwards BGR, bottom to top order to a PNG.
45 void writePngFromBitmapPixels(Pixel)
46 (Pixel[] pix, File handle, int width, int height) {
47     immutable nPixels = width * height;
48     enforce(pix.length == nPixels);
49 
50     ubyte[] rgbPix = (cast(ubyte*) malloc(nPixels * 4))[0..nPixels * 4];
51     scope(exit) free(cast(void*) rgbPix);
52     size_t copyIndex = 0;
53 
54     // We need to make the image right side up, not upside down like they are
55     // for bitmaps.  We also need to make sure the byte order is rgb, not bgr,
56     // and add an alpha channel.
57     foreach(row; 0..height) {
58         auto rowPix = pix[$ - (row + 1) * width..$ - row * width];
59 
60         foreach(p; rowPix) {
61             rgbPix[copyIndex++] = p.r;
62             rgbPix[copyIndex++] = p.g;
63             rgbPix[copyIndex++] = p.b;
64             rgbPix[copyIndex++] = 255;
65         }
66     }
67 
68     TrueColorImage png;
69     png.width = width;
70     png.height = height;
71     png.data = rgbPix;
72 
73     auto pngData = writePng(pngFromImage(png));
74     handle.rawWrite(pngData);
75 }
76 
77 
78 // The stuff below is by Adam D. Ruppe, 2009-2010, released into the public
79 // domain, adapted by David Simcha 2010.
80 import std.zlib;
81 
82 // convenience for 32 bit per pixel RGBA images
83 struct TrueColorImage {
84 	int width;
85 	int height;
86 	ubyte[] data; // one byte per color entry, left to right, top to bottom, RGBA. So 32 bits per pixel
87 	// I believe this is, in memory, identical to Color[] data;
88 }
89 
90 // Represents basic data about a png. If you just use the convenience structs above, you never actually need this.
91 struct PNGHeader {
92 	uint width; // in pixels
93 	uint height;
94 	ubyte depth = 8; // this is bits per color entry. For truecolor, 8 means RGBA each get 8 bits each. For indexed images, it is bits per pixel, so 8 means 256 color. It must be a power of two. 8 almost always works for me.
95 	ubyte type = 6; // 0 - greyscale, 2 - truecolor, 3 - indexed color, 4 - grey with alpha, 6 - true with alpha
96 	ubyte compressionMethod = 0; // should be zero
97 	ubyte filterMethod = 0; // should be zero
98 	ubyte interlaceMethod = 0; // bool. this code doesn't support interlacing.
99 }
100 
101 // Ditto, but for truecolor images
102 PNG* pngFromImage(TrueColorImage i) {
103 	PNGHeader h;
104 	h.width = i.width;
105 	h.height = i.height;
106 	// FIXME: optimize it if it is greyscale or doesn't use alpha alpha
107 
108 	auto png = blankPNG(h);
109 	addImageDatastreamToPng(i.data, png);
110 
111 	return png;
112 }
113 
114 // used when reading a png file
115 PNGHeader getHeader(PNG* p) {
116 	PNGHeader h;
117 	ubyte[] data = p.getChunk("IHDR").payload;
118 
119 	int pos = 0;
120 
121 	h.width |= data[pos++] << 24;
122 	h.width |= data[pos++] << 16;
123 	h.width |= data[pos++] << 8;
124 	h.width |= data[pos++] << 0;
125 
126 	h.height |= data[pos++] << 24;
127 	h.height |= data[pos++] << 16;
128 	h.height |= data[pos++] << 8;
129 	h.height |= data[pos++] << 0;
130 
131 	h.depth = data[pos++];
132 	h.type = data[pos++];
133 	h.compressionMethod = data[pos++];
134 	h.filterMethod = data[pos++];
135 	h.interlaceMethod = data[pos++];
136 
137 	return h;
138 }
139 
140 
141 // PNG files are made out of a series of chunks
142 struct Chunk {
143 	uint size;
144 	ubyte[4] type;
145 	ubyte[] payload;
146 	uint checksum;
147 }
148 
149 // the file has a magic number header, then a bunch of chunks. This wraps
150 // that up for you - you should never need to pry into the insides of this for
151 // simple images
152 struct PNG {
153 	uint length;
154 	ubyte[8] header;
155 	Chunk[] chunks;
156 
157 	Chunk* getChunk(string what) {
158 		foreach(ref c; chunks) {
159 			if(cast(string) c.type == what)
160 				return &c;
161 		}
162 		throw new Exception("no such chunk " ~ what);
163 	}
164 
165 	Chunk* getChunkNullable(string what) {
166 		foreach(ref c; chunks) {
167 			if(cast(string) c.type == what)
168 				return &c;
169 		}
170 		return null;
171 	}
172 }
173 
174 // Takes a png in memory, and translates it into an array suitable for immediate writing to a file.
175 // std.file.write("my-image.png", writePng(png));
176 ubyte[] writePng(PNG* p) {
177 	ubyte[] a;
178 	if(p.length)
179 		a.length = p.length;
180 	else {
181 		a.length = 8;
182 		foreach(c; p.chunks)
183 			a.length += c.size + 12;
184 	}
185 	uint pos;
186 
187 	a[0..8] = p.header[0..8];
188 	pos = 8;
189 	foreach(c; p.chunks) {
190 		a[pos++] = (c.size & 0xff000000) >> 24;
191 		a[pos++] = (c.size & 0x00ff0000) >> 16;
192 		a[pos++] = (c.size & 0x0000ff00) >> 8;
193 		a[pos++] = (c.size & 0x000000ff) >> 0;
194 
195 		a[pos..pos+4] = c.type[0..4];
196 		pos += 4;
197 		a[pos..pos+c.size] = c.payload[0..c.size];
198 		pos += c.size;
199 
200 		a[pos++] = (c.checksum & 0xff000000) >> 24;
201 		a[pos++] = (c.checksum & 0x00ff0000) >> 16;
202 		a[pos++] = (c.checksum & 0x0000ff00) >> 8;
203 		a[pos++] = (c.checksum & 0x000000ff) >> 0;
204 	}
205 
206 	return a;
207 }
208 
209 // If you just want to handle the data yourself, use this to get a struct ready for addition of other chunks. This function creates the required header chunk based on a struct you pass in.
210 /*
211 
212 PNGHeader h;
213 
214 h.width = desiredWidth;
215 h.height = desiredHeight;
216 // the default values of the PNGHeader struct will suffice for everything else. The default is 32 bits per pixel, RGBA - see the source for more info
217 
218 auto png = blankPNG(h); // creates a PNG* from the given header
219 
220 addImageDatastreamToPng(pixels, png); // pixels is a ubyte[] representing the raw image data
221 
222 std.file.write("myimage.png", writePng(png));
223 */
224 PNG* blankPNG(PNGHeader h) {
225 	auto p = new PNG;
226 	p.header = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
227 
228 	Chunk c;
229 
230 	c.size = 13;
231 	c.type = ['I', 'H', 'D', 'R'];
232 
233 	c.payload.length = 13;
234 	int pos = 0;
235 
236 	c.payload[pos++] = h.width >> 24;
237 	c.payload[pos++] = (h.width >> 16) & 0xff;
238 	c.payload[pos++] = (h.width >> 8) & 0xff;
239 	c.payload[pos++] = h.width & 0xff;
240 
241 	c.payload[pos++] = h.height >> 24;
242 	c.payload[pos++] = (h.height >> 16) & 0xff;
243 	c.payload[pos++] = (h.height >> 8) & 0xff;
244 	c.payload[pos++] = h.height & 0xff;
245 
246 	c.payload[pos++] = h.depth;
247 	c.payload[pos++] = h.type;
248 	c.payload[pos++] = h.compressionMethod;
249 	c.payload[pos++] = h.filterMethod;
250 	c.payload[pos++] = h.interlaceMethod;
251 
252 
253 	c.checksum = crc("IHDR", c.payload);
254 
255 	p.chunks ~= c;
256 
257 	return p;
258 }
259 
260 // should NOT have any idata already.
261 // FIXME: doesn't handle palettes
262 
263 /**Adds a byte array of image data to the given PNG. You are responsible for
264  * ensuring that the data array is in the correct format.
265  */
266 void addImageDatastreamToPng(const(ubyte)[] data, PNG* png) {
267 	// we need to go through the lines and add the filter byte
268 	// then compress it into an IDAT chunk
269 	// then add the IEND chunk
270 
271 	PNGHeader h = getHeader(png);
272 
273 	auto bytesPerLine = h.width * 4;
274 	if(h.type == 3)
275 		bytesPerLine = h.width * 8 /  h.depth;
276 	Chunk dat;
277 	dat.type = ['I', 'D', 'A', 'T'];
278 	int pos = 0;
279 
280 	const(ubyte)[] output;
281 	while(pos+bytesPerLine <= data.length) {
282 		output ~= 0;
283 		output ~= data[pos..pos+bytesPerLine];
284 		pos += bytesPerLine;
285 	}
286 
287 	auto com = cast(ubyte[]) compress(output);
288 	dat.size = com.length;
289 	dat.payload = com;
290 	dat.checksum = crc("IDAT", dat.payload);
291 
292 	png.chunks ~= dat;
293 
294 	Chunk c;
295 
296 	c.size = 0;
297 	c.type = ['I', 'E', 'N', 'D'];
298 	c.checksum = crc("IEND", c.payload);
299 
300 	png.chunks ~= c;
301 
302 }
303 
304 /* CRC function, adapted from example C source in the PNG spec. */
305 
306 uint update_crc(in uint crc, in ubyte[] buf){
307 	static immutable uint[256] crc_table =
308         [0, 1996959894, 3993919788, 2567524794, 124634137, 1886057615,
309          3915621685, 2657392035, 249268274, 2044508324, 3772115230, 2547177864,
310          162941995, 2125561021, 3887607047, 2428444049, 498536548, 1789927666,
311          4089016648, 2227061214, 450548861, 1843258603, 4107580753, 2211677639,
312          325883990, 1684777152, 4251122042, 2321926636, 335633487, 1661365465,
313          4195302755, 2366115317, 997073096, 1281953886, 3579855332, 2724688242,
314          1006888145, 1258607687, 3524101629, 2768942443, 901097722, 1119000684,
315          3686517206, 2898065728, 853044451, 1172266101, 3705015759, 2882616665,
316          651767980, 1373503546, 3369554304, 3218104598, 565507253, 1454621731,
317          3485111705, 3099436303, 671266974, 1594198024, 3322730930, 2970347812,
318          795835527, 1483230225, 3244367275, 3060149565, 1994146192, 31158534,
319          2563907772, 4023717930, 1907459465, 112637215, 2680153253, 3904427059,
320          2013776290, 251722036, 2517215374, 3775830040, 2137656763, 141376813,
321          2439277719, 3865271297, 1802195444, 476864866, 2238001368, 4066508878,
322          1812370925, 453092731, 2181625025, 4111451223, 1706088902, 314042704,
323          2344532202, 4240017532, 1658658271, 366619977, 2362670323, 4224994405,
324          1303535960, 984961486, 2747007092, 3569037538, 1256170817, 1037604311,
325          2765210733, 3554079995, 1131014506, 879679996, 2909243462, 3663771856,
326          1141124467, 855842277, 2852801631, 3708648649, 1342533948, 654459306,
327          3188396048, 3373015174, 1466479909, 544179635, 3110523913, 3462522015,
328          1591671054, 702138776, 2966460450, 3352799412, 1504918807, 783551873,
329          3082640443, 3233442989, 3988292384, 2596254646, 62317068, 1957810842,
330          3939845945, 2647816111, 81470997, 1943803523, 3814918930, 2489596804,
331          225274430, 2053790376, 3826175755, 2466906013, 167816743, 2097651377,
332          4027552580, 2265490386, 503444072, 1762050814, 4150417245, 2154129355,
333          426522225, 1852507879, 4275313526, 2312317920, 282753626, 1742555852,
334          4189708143, 2394877945, 397917763, 1622183637, 3604390888, 2714866558,
335          953729732, 1340076626, 3518719985, 2797360999, 1068828381, 1219638859,
336          3624741850, 2936675148, 906185462, 1090812512, 3747672003, 2825379669,
337          829329135, 1181335161, 3412177804, 3160834842, 628085408, 1382605366,
338          3423369109, 3138078467, 570562233, 1426400815, 3317316542, 2998733608,
339          733239954, 1555261956, 3268935591, 3050360625, 752459403, 1541320221,
340          2607071920, 3965973030, 1969922972, 40735498, 2617837225, 3943577151,
341          1913087877, 83908371, 2512341634, 3803740692, 2075208622, 213261112,
342          2463272603, 3855990285, 2094854071, 198958881, 2262029012, 4057260610,
343          1759359992, 534414190, 2176718541, 4139329115, 1873836001, 414664567,
344          2282248934, 4279200368, 1711684554, 285281116, 2405801727, 4167216745,
345          1634467795, 376229701, 2685067896, 3608007406, 1308918612, 956543938,
346          2808555105, 3495958263, 1231636301, 1047427035, 2932959818, 3654703836,
347          1088359270, 936918000, 2847714899, 3736837829, 1202900863, 817233897,
348          3183342108, 3401237130, 1404277552, 615818150, 3134207493, 3453421203,
349          1423857449, 601450431, 3009837614, 3294710456, 1567103746, 711928724,
350          3020668471, 3272380065, 1510334235, 755167117];
351 
352 	uint c = crc;
353 
354 	foreach(b; buf)
355 		c = crc_table[(c ^ b) & 0xff] ^ (c >> 8);
356 
357 	return c;
358 }
359 
360 // lol is just the chunk name
361 uint crc(in string lol, in ubyte[] buf){
362 	uint c = update_crc(0xffffffffL, cast(ubyte[]) lol);
363 	return update_crc(c, buf) ^ 0xffffffffL;
364 }
365 
366 }