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 }