root/branches/lodepng/trunk/png.d

Revision 232, 39.1 kB (checked in by Gregor, 2 years ago)

Added LodePNG

Line 
1 /**
2 LodePNG version 20070102
3
4 Copyright (c) 2005-2007 Lode Vandevenne
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
8
9   * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
10   * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
11   * Neither the name of Lode Vandevenne nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12
13 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
15 Port to D by Lutger Blijdestijn on 5 januari 2007.<br>
16 The port is close to the original, but the api has changed. I have taken out the class and made the api into two functions instead.
17 Also, the Encoder is (not yet) ported. Implementation-wise zlib routines are replaced by std.zlib, but the rest is the same.
18 Here follows part of the original documentation, slightly modified to reflect the D port.<br>
19 You can email me at: lutger dot blijdenstijn at gmail dot com
20
21 About:
22
23 PNG is a file format to store raster images losslessly with good compression,
24 supporting different color types. It's patent-free. Because the compression is
25 lossless and it supports an alpha channel, PNG is a very good file format
26 to store textures and tilesets for computer games.
27
28 LodePNG is a PNG codec according to the Portable Network Graphics (PNG)
29 Specification (Second Edition) - W3C Recommendation 10 November 2003. The
30 specification can be found on line at http://www.w3.org/TR/2003/REC-PNG-20031110
31
32 The most recent version of LodePNG can currently be found at http://student.kuleuven.be/~m0216922/pngloader/
33 However, this location is temporary and won't exist anymore in 2007.
34
35 LodePNG exists out of the source code file lodepng.d
36
37 LodePNG is simple but only supports the basic requirements. In the tradeoff
38 between functionality and simplicity, LodePNG is on the side of simplicity.
39 To achieve this simplicity, the following design choices were made: There
40 are no dependencies on any external library. To decode PNGs, there's a Decoder
41 class (edit: in D port no class) that can convert any PNG file data into an RGBA image buffer with a single
42 function call.
43
44 This all makes LodePNG suitable for loading textures in games, raytracers,
45 intros, or for loading images into programs that require them only for simple
46 usage. It's less suitable for full fledged image editors, loading PNGs over
47 network (since this decoder requires all the image data to be available before
48 the decoding can begin), life-critical systems, ... Even though it contains
49 a conformant decoder and encoder (not ported for D yet), it's still not a conformant editor,
50 because unknown chunks are discarded.
51
52 Because LodePNG is BSD licensed, it's allowed to use these source files in any
53 project, as long as you include the above copyright message and conditions
54 somewhere in the release of your program.
55
56 <b>Supported features</b><br>
57 The following features are supported by the decoder:
58 <UL>
59   <li> conformant decoding of PNGs with any color type, bit depth and interlace mode</li>
60   <li> converting the result to 32-bit RGBA pixel data</li>
61   <li> decoding a png image from a buffer.</li>
62   <li> support for translucent PNG's, including translucent palettes and color key</li>
63   <li> zlib decompression (inflate) (via phobos in D)</li>
64   <li> 64 bit color (untested due to lack of such images)</li>
65   <li> the following chunks are interpreted by the decoder
66   <ul>
67     <li>IHDR (header information)</li>
68     <li>PLTE (color palette)</li>
69     <li>IDAT (pixel data)</li>
70     <li>IEND (the final chunk)</li>
71     <li>tRNS (transparency for palettized images)</li>
72     <li>bKGD (suggested background color)</li>
73     </ul>
74    </li>
75 </UL>
76
77 <b>Features not supported</b><br>
78 The following features are _not_ supported.
79 <ul>
80 <li> editing a PNG image (unless you use the decoder, then edit it, then use the
81     encoder, but ignored chunks will then be gone from the original image)
82 <li> CRC checks of chunks
83 <li> ADLER32 checksum of zlib data
84 <li> partial loading. All data must be available and is processed in one call.
85 <li> The following optional chunk types are not interpreted by the decoder <ul>
86     <li>cHRM (device independent color info)
87     <li>gAMA (device independent color info)
88     <li>iCCP (device independent color info)
89     <li>sBIT (original number of significant bits)
90     <li>sRGB (device independent color info)
91     <li>tEXt (textual information)
92     <li>zTXt (compressed textual information)
93     <li>iTXt (international textual information)
94     <li>hIST (palette histogram)
95     <li>pHYs (physical pixel dimensions)
96     <li>sPLT (suggested reduced palette)
97     <li>tIME (last image modification time)
98     </ul>
99 </ul>
100
101 Usage:
102
103 The basic steps of decoding a PNG file with LodePNG are as follows:
104 <OL>
105 <LI> Load the PNG image file from harddisk and store the bytes of the file in an ubyte[]
106  with the same size as the file.
107 <LI> Declare another ubyte[] that will contain the pixel data.
108 <LI> Call the decode function of it with the above arrays as parameters and store the
109 resulting Info struct.
110 <LI> Check for errors with in Info.error.
111 <LI> If no error occurred, the raw pixel data is now stored in the output array.
112 </OL>
113
114 To decode a PNG file, you can use one of the two decode functions.
115 The function decode32 will store the pixel data in 32 bit RGBA format, no
116 matter what color format was used in the PNG file. The function decodeGeneric
117 will store the pixel data in the same color format as in the file. A more
118 detailed description of both functions, as well as the optional loadFile
119 function, is given below.
120 */
121 module png;
122 import std.zlib;
123
124 /**Information about the PNG image is returned in the form of an Info struct.
125
126 After using decodeGeneric or decode32, please always check the error value
127 to see if everything went ok.
128 */
129 struct Info
130 {
131     /** Below is a table that gives the meaning of the different values.
132
133     In the port to D, some of these errors have become obsolete as zlib has replaced a few routines and will spit out
134     exceptions instead. I have not yet removed these.
135
136 <li> 0: no error, everything went ok
137 <li> 1: there is no PNG loaded yet, so it makes no sense trying to read any information values
138 <li> 10: while huffman decoding: end of input memory reached without endcode
139 <li> 11: while huffman decoding: error in code tree made it jump outside of tree
140 <li> 13: problem while processing dynamic deflate block
141 <li> 14: problem while processing dynamic deflate block
142 <li> 15: problem while processing dynamic deflate block
143 <li> 16: unexisting code while processing dynamic deflate block
144 <li> 17: while inflating: end of out buffer memory reached
145 <li> 18: while inflating: invalid distance code
146 <li> 19: while inflating: end of out buffer memory reached
147 <li> 20: invalid deflate block BTYPE encountered
148 <li> 21: NLEN is not ones complement of LEN in a deflate block
149 <li> 22: while inflating: end of out buffer memory reached.
150    This can happen if the inflated deflate data is longer than the amount of bytes required to fill up
151    all the pixels of the image, given the color depth and image dimensions. Something that doesn't
152    happen in a normal, well encoded, PNG image.
153 <li> 23: while inflating: end of in buffer memory reached
154 <li> 24: invalid FCHECK in zlib header
155 <li> 25: invalid compression method in zlib header
156 <li> 26: FDICT encountered in zlib header while it's not used for PNG
157 <li> 27: PNG file is smaller than a PNG header
158 <li> 28: incorrect PNG signature (the first 8 bytes of the PNG file)
159    Maybe it's not a PNG, or a PNG file that got corrupted so that the header indicates the corruption.
160 <li> 29: first chunk is not the header chunk
161 <li> 30: chunk length too large, chunk broken off at end of file
162 <li> 31: illegal PNG color type
163 <li> 32: illegal PNG compression method
164 <li> 33: illegal PNG filter method
165 <li> 34: illegal PNG interlace method
166 <li> 35: chunk length of a chunk is too large or the chunk too small
167 <li> 36: illegal PNG filter type encountered
168 <li> 37: illegal bit depth for this color type given
169 <li> 38: the palette is too big (more than 256 colors)
170 <li> 39: more palette alpha values given in tRNS, than there are colors in the palette
171 <li> 40: tRNS chunk has wrong size for greyscale image
172 <li> 41: tRNS chunk has wrong size for RGB image
173 <li> 42: tRNS chunk appeared while it was not allowed for this color type
174 <li> 43: bKGD chunk has wrong size for palette image
175 <li> 44: bKGD chunk has wrong size for greyscale image
176 <li> 45: bKGD chunk has wrong size for RGB image
177 <li> 46: value encountered in indexed image is larger than the palette size (bitdepth == 8)
178 <li> 47: value encountered in indexed image is larger than the palette size (bitdepth < 8)
179 <li> 48: the input data is empty. Maybe a PNG file you tried to load doesn't exist or is in the wrong path.
180 <li> 49: jumped past memory while generating dynamic huffman tree
181 <li> 50: jumped past memory while generating dynamic huffman tree
182 <li> 51: jumped past memory while inflating huffman block
183 <li> 52: jumped past memory while inflating
184 <li> 53: size of zlib data too small to contain header
185 <li> 54: CHAR_BITS is smaller than 8 on this platform, an 8-bit datatype is needed to store the file and pixel data
186 <li>55: jumped past tree while generating huffman tree, this could be when the code
187        lengths are not of an optimal_ tree, which causes there to be more nodes than
188        the arrays used can support (e.g. if there are 19 codes a tree has 18 nodes,
189        but if all 19 code lengths are 7, there will be much more nodes), this may be a
190        BUG that has to be fixed though I haven't actually encountered PNGs causing
191        this problem outside the experimentation environment
192 <li> 56: As of LodePNG version 20061209, the input and output parameter of the decode functions got swapped!
193        Try to swap the input and output parameter in the decode function call. (first out, then in)
194        If they are already correct and you get this error, then see description of error 48.
195     */
196     int error = 1; //the error value of the decode attempt
197
198     // dimensions of the image
199     size_t w; /// width of the image in pixels
200     size_t h; /// height of the image in pixels
201
202     //pixel color info
203     uint bitDepth;      /// bits per sample
204     uint bpp;           /// bits per pixel
205     uint colorChannels; /// amount of channels
206
207     //palette info
208     uint paletteSize; /// size of the palette (palette.size() / 4)
209     ubyte[] palette;  /// palette in RGBARGBA... order
210
211     //transparent color key info
212     bool  colorKey; /// is a transparent color key given?
213     ubyte keyR;     /// red/greyscale component of color key
214     ubyte keyG;     /// green component of color key
215     ubyte keyB;     /// blue component of color key
216
217     //PNG specific information (general)
218     uint colorType;         /// color type of the original PNG file
219     uint compressionMethod; /// compression method of the original file
220     uint filterMethod;      /// filter method of the original file
221     uint interlaceMethod;   /// interlace method of the original file
222
223     //PNG specific information (bKGD chunk)
224     bool  backgroundColor; /// is a suggested background color given?
225     ubyte backgroundR;     /// red component of sugg. background color
226     ubyte backgroundG;     /// green component of sugg. background color
227     ubyte backgroundB;     /// blue component of sugg. background color
228 }
229 /**This function converts the given compressed PNG data into uncompressed pixel
230 data. The original color type is preserved, which can make it more difficult
231 to interpret the pixel data since you have to take into account what color
232 type it is.
233
234 For normal usage of LodePNG, this function is *not* recommended, but in special
235 cases where you know beforehand what color type the PNG image has it can provide
236 an increase in efficiency. Use decode32 instead if you want any type of PNG
237 to be easily displayed! The decode32 function first calls decodeGeneric
238 and then does the job of converting all possible color types to RGBA.
239
240 Params:
241 _in = This is used to give the original file data.
242  Provide a vector filled with the contents of a PNG file. The contents of
243  this buffer will be modified during the decoding process, so it cannot be
244  reused. If you need the file contents a second time, make a copy before
245  decoding the PNG or reload the file.
246
247 _out = This is used to return the pixel data.
248  Provide an empty vector. The vector will be resized and the uncompressed
249  pixel data will be stored in the buffer. The image data will be of the same
250  color type as the PNG image. Pixels are always stored row by row. The first
251  byte of the data contains the first byte of the pixel of the top left of
252  the image. 1-bit images or other formats with less than one byte per pixel
253  will be stored bit per bit. In that case, scanlines don't necessarily start
254  at the boundary of a  byte. If RGB or RGBA is used, color components per pixel
255  are successive bits/bytes in the order R, G, B, A.
256 */
257 Info decodeGeneric(inout ubyte[] _out, inout ubyte[] _in)
258 {
259     Info info;
260   //read the information from the header and store it in the Info
261     void readPngHeader(ubyte* _in, size_t inlength)
262     {
263       if(inlength < 29) { info.error = 27; return; } //error: the data length is smaller than the length of the header
264
265       if(_in[0] != 137 || _in[1] != 80 || _in[2] != 78 || _in[3] != 71 || _in[4] != 13 || _in[5] != 10 || _in[6] != 26 || _in[7] != 10) { info.error = 28; return; } //error: the first 8 bytes are not the correct PNG signature
266       if(_in[12] != 73 || _in[13] != 72 || _in[14] != 68 || _in[15] != 82) { info.error = 29; return; } //error: it doesn't start with a IHDR chunk!
267
268       //read the values given in the header
269       info.w = 256 * 256 * 256 * _in[16] + 256 * 256 * _in[17] + 256 * _in[18] + _in[19];
270       info.h = 256 * 256 * 256 * _in[20] + 256 * 256 * _in[21] + 256 * _in[22] + _in[23];
271       info.bitDepth = _in[24];
272       info.colorType = _in[25];
273       info.compressionMethod = _in[26];
274       info.filterMethod = _in[27];
275       info.interlaceMethod = _in[28];
276       //The 4 CRC bytes are ignored
277
278       if(info.compressionMethod != 0) { info.error = 32; return; } //error: only compression method 0 is allowed in the specification
279       if(info.filterMethod != 0)      { info.error = 33; return; } //error: only filter method 0 is allowed in the specification
280       if(info.interlaceMethod > 1)    { info.error = 34; return; } //error: only interlace methods 0 and 1 exist in the specification
281
282       //check if bit depth is valid for this color type, if not give error 37 "illegal bit depth for this color type given"
283       uint bd = info.bitDepth; //longer variable name makes code below more easily readable
284       switch(info.colorType)
285       {
286         case 0: if(bd != 1 && bd != 2 && bd != 4 && bd != 8 && bd != 16) { info.error = 37; return; } break;
287         case 2: if(                                 bd != 8 && bd != 16) { info.error = 37; return; } break;
288         case 3: if(bd != 1 && bd != 2 && bd != 4 && bd != 8            ) { info.error = 37; return; } break;
289         case 4: if(                                 bd != 8 && bd != 16) { info.error = 37; return; } break;
290         case 6: if(                                 bd != 8 && bd != 16) { info.error = 37; return; } break;
291         default: { info.error = 31; return; } break; //error: invalid color type
292       }
293     }
294
295     //filter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1)
296     //precon is the previous filtered scanline, recon the result, scanline the current one
297     void unFilterScanline(ubyte[] recon, ubyte[] scanline, ubyte[] precon, bool top, size_t bytewidth, uint filterType, size_t length)
298     {
299       switch(filterType)
300       {
301         case 0:
302           for(size_t i = 0; i < length; i++) recon[i] = scanline[i];
303           break;
304         case 1:
305           if(top)
306           {
307             for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i];
308             for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth];
309           }
310           else
311           {
312             for(size_t i =         0; i < bytewidth; i++) recon[i] = scanline[i];
313             for(size_t i = bytewidth; i < length   ; i++) recon[i] = scanline[i] + recon[i - bytewidth];
314           }
315           break;
316         case 2:
317           if(top) for(size_t i = 0; i < length; i++) recon[i] = scanline[i];
318           else for(size_t i = 0; i < length; i++) recon[i] = scanline[i] + precon[i];
319           break;
320         case 3:
321           if(top)
322           {
323             for(size_t i = 0; i < length; i++) recon[i] = scanline[i];
324             for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] >> 1;
325           }
326           else
327           {
328             for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2;
329             for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1);
330           }
331           break;
332         case 4:
333           if(top)
334           {
335             for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i];
336             for(size_t i = bytewidth; i < length; i++) recon[i] = cast(ubyte)(scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0));
337           }
338           else
339           {
340             for(size_t i = 0; i < bytewidth; i++) recon[i] = cast(ubyte)(scanline[i] + paethPredictor(0, precon[i], 0));
341             for(size_t i = bytewidth; i < length; i++) recon[i] = cast(ubyte)(scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
342           }
343           break;
344       default: info.error = 36; return; //error: unexisting filter type given
345       }
346     }
347
348     //filter and reposition the pixels into the output when the image is Adam7 interlaced. This function can only do it after the full image is already decoded. The out buffer must have the correct allocated memory size already.
349     void adam7Pass(ubyte[] _out, ubyte[] _in, ubyte[] scanlinen, ubyte[] scanlineo, size_t w, size_t bytewidth, size_t passleft, size_t passtop, size_t spacex, size_t spacey, size_t passw, size_t passh, uint bpp)
350     {
351       for(uint s = 0; s < passh; s++)
352       {
353         size_t linelength = 1 + ((bpp * passw + 7) >> 3);
354         size_t linestart = s * linelength; //position where we read the filterType: at the start of the scanline
355         uint filterType = _in[linestart];
356
357         unFilterScanline(scanlinen, _in[linestart + 1..$], scanlineo, (s < 1), bytewidth, filterType, (w * bpp + 7) >> 3);
358         if(info.error) return;
359
360         //put the filtered pixels in the output image
361         if(bpp >= 8)
362         {
363           for(size_t i = 0; i < passw; i++)
364           for(size_t b = 0; b < bytewidth; b++) //b = current byte of this pixel
365           {
366             _out[bytewidth * w * (passtop + spacey * s) + bytewidth * (passleft + spacex * i) + b] = scanlinen[bytewidth * i + b];
367           }
368         }
369         else
370         {
371           for(size_t i = 0; i < passw; i++)
372           {
373             size_t outbitp = bpp * w * (passtop + spacey * s) + bpp * (passleft + spacex * i);
374             for(size_t b = 0; b < bpp; b++) //b = current bit of this pixel
375             {
376               size_t obp = outbitp + b;
377               size_t obitpos = 7 - (obp & 0x7); //where bitpos 0 refers to the LSB, bitpot 7 to the MSB of a byte
378               size_t bp = i * bpp + b;
379               size_t bitpos = 7 - (bp & 0x7); //where bitpos 0 refers to the LSB, bitpot 7 to the MSB of a byte
380               uint _bit = (scanlinen[bp >> 3] >> bitpos) & 1;
381               _out[obp >> 3] = cast(ubyte)((_out[obp >> 3] & ~(1 << obitpos)) | (_bit << obitpos));
382             }
383           }
384         }
385
386         //swap the two buffer pointers "scanline old" and "scanline new"
387         ubyte[] temp = scanlinen;
388         scanlinen = scanlineo;
389         scanlineo = temp;
390       }
391     }
392
393     void resetParameters()
394     {
395       info.error = 0; //initially no error happened yet
396       info.paletteSize = info.backgroundColor = info.colorKey = 0; //initialize info variables that aren't necessarily set later on
397     }
398
399   //if(CHAR_BIT < 8) { info.error = 54; return; } // not relevant in D, type sizes are guaranteed
400   if(_in.length == 0 && _out.length > 0) { info.error = 56; return info; } //the given input data is empty but the output data not
401   if(_in.length == 0) { info.error = 48; return info; } //the given data is empty
402
403   resetParameters(); //when decoding a new PNG image, make sure all parameters created after previous decoding are reset
404
405   readPngHeader(_in.ptr, _in.length);
406   if(info.error) return info;
407
408   size_t pos = 33; //first byte of the first chunk after the header
409   size_t dataPos = 0; //at this position in the in buffer the new data will be stored.
410   size_t dataStart = 0;
411
412   bool IEND = 0;
413   while(!IEND) //loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer
414   {
415     //get chunk length
416     if(pos + 8 >= _in.length) { info.error = 30; return info; } //error: size of the in buffer too small to contain next chunk
417     size_t chunkLength = 256 * 256 * 256 * _in[pos] + 256 * 256 * _in[pos + 1] + 256 * _in[pos + 2] + _in[pos + 3]; pos += 4;
418     if(pos + chunkLength >= _in.length) { info.error = 35; return info; } //error: size of the in buffer too small to contain next chunk
419
420     //IDAT chunk, containing compressed image data
421     if(_in[pos + 0] == 'I' && _in[pos + 1] == 'D' && _in[pos + 2] == 'A' && _in[pos + 3] == 'T')
422     {
423       pos += 4;
424       if(dataPos == 0) { dataStart = pos; dataPos = dataStart + chunkLength; pos += chunkLength; } //possible efficiency increase by not copying data from first chunk
425       else for(size_t i = 0; i < chunkLength; i++) _in[dataPos++] = _in[pos++]; //multiple data chunks are added behind each other this way
426     }
427     //IEND chunk
428     else if(_in[pos + 0] == 'I' && _in[pos + 1] == 'E' && _in[pos + 2] == 'N' && _in[pos + 3] == 'D')
429     {
430       IEND = 1;
431     }
432     //palette chunk (PLTE)
433     else if(_in[pos + 0] == 'P' && _in[pos + 1] == 'L' && _in[pos + 2] == 'T' && _in[pos + 3] == 'E')
434     {
435       pos += 4; //go after the 4 letters
436       info.paletteSize = chunkLength / 3;
437       if(info.paletteSize > 256) { info.error = 38; return info; } //error: palette too big
438       info.palette.length = 4 * info.paletteSize;
439       for(size_t i = 0; i < info.paletteSize; i++)
440       {
441         info.palette[i * 4 + 0] = _in[pos++]; //R
442         info.palette[i * 4 + 1] = _in[pos++]; //G
443         info.palette[i * 4 + 2] = _in[pos++]; //B
444         info.palette[i * 4 + 3] = 255; //alpha
445       }
446     }
447     //palette transparency chunk (tRNS)
448     else if(_in[pos + 0] == 't' && _in[pos + 1] == 'R' && _in[pos + 2] == 'N' && _in[pos + 3] == 'S')
449     {
450       pos += 4; //go after the 4 letters
451       if(info.colorType == 3)
452       {
453         if(chunkLength > info.paletteSize) { info.error = 39; return info; } //error: more alpha values given than there are palette entries
454         for(size_t i = 0; i < chunkLength; i++) info.palette[i * 4 + 3] = _in[pos++];
455       }
456       else if(info.colorType == 0)
457       {
458         if(chunkLength != 2) { info.error = 40; return info; } //error: this chunk must be 2 bytes for greyscale image
459         info.colorKey = 1;
460         info.keyR = 256 * _in[pos] + _in[pos + 1]; pos += 2;
461       }
462       else if(info.colorType == 2)
463       {
464         if(chunkLength != 6) { info.error = 41; return info; } //error: this chunk must be 6 bytes for RGB image
465         info.colorKey = 1;
466         info.keyR = 256 * _in[pos] + _in[pos + 1]; pos += 2;
467         info.keyG = 256 * _in[pos] + _in[pos + 1]; pos += 2;
468         info.keyB = 256 * _in[pos] + _in[pos + 1]; pos += 2;
469       }
470       else { info.error = 42; return info; } //error: tRNS chunk not allowed for other color models
471     }
472     //background color chunk (bKGD)
473     else if(_in[pos + 0] == 'b' && _in[pos + 1] == 'K' && _in[pos + 2] == 'G' && _in[pos + 3] == 'D')
474     {
475       pos += 4; //go after the 4 letters
476       if(info.colorType == 3)
477       {
478         if(chunkLength != 1) { info.error = 43; return info; } //error: this chunk must be 1 byte for indexed color image
479         info.backgroundColor = 1;
480         info.backgroundR = _in[pos++];
481       }
482       else if(info.colorType == 0 || info.colorType == 4)
483       {
484         if(chunkLength != 2) { info.error = 44; return info; } //error: this chunk must be 2 bytes for greyscale image
485         info.backgroundColor = 1;
486         info.backgroundR = 256 * _in[pos] + _in[pos + 1]; pos += 2;
487       }
488       else if(info.colorType == 2 || info.colorType == 6)
489       {
490         if(chunkLength != 6) { info.error = 45; return info; } //error: this chunk must be 6 bytes for greyscale image
491         info.backgroundColor = 1;
492         info.backgroundR = 256 * _in[pos] + _in[pos + 1]; pos += 2;
493         info.backgroundG = 256 * _in[pos]