root/trunk/lodepng/lodepng/Common.d

Revision 484, 24.2 kB (checked in by Lutger, 1 month ago)

bugfix: bytesPerPixel

Line 
1 // written in the D programming language
2
3 /***************************************************************************************************
4 Types and functions common to both the encode and decoder of lodepng, as well as image format
5 conversion routines
6
7 License:
8 Copyright (c) 2005-2007 Lode Vandevenne
9 All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
12
13   - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.<br>
14   - 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.<br>
15   - 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.<br>
16
17 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.
18
19 Authors: Lode Vandevenne (original version in C++), Lutger Blijdestijn (D version) : lutger dot blijdestijn at gmail dot com,
20   Stewart Gordon (modifications)
21
22 Date: August 7, 2007
23
24 References:
25 $(LINK2 http://members.gamedev.net/lode/projects/LodePNG/, Original lodepng) <br>
26 $(LINK2 http://www.w3.org/TR/PNG/, PNG Specification) <br>
27 $(LINK2 http://www.libpng.org/pub/png/pngsuite.html, PNG Suite: set of test images) <br>
28 $(LINK2 http://optipng.sourceforge.net/, OptiPNG: tool to experimentally optimize png images)
29 ***************************************************************************************************/
30 module lodepng.Common;
31 import lodepng.util;
32 import lodepng.ZlibCodec;
33
34 /***************************************************************************************************
35     Png specific exception.
36
37         Instead of errors codes, this port of lodepng makes use of exceptions.
38         At the moment, the decoder is very strict and will tolerate no errors whatsoever even
39         if they could safely be ignored. CRC checking is always done.
40         This might be slightly relaxed in a future release to be more in line with the
41         recommendations of the specification.
42
43 ***************************************************************************************************/
44 class PngException : Exception
45 {
46     this(char[] msg)
47     {
48         super(msg);
49     }
50 }
51
52 package alias _enforce!(PngException) pngEnforce;
53
54 /***************************************************************************************************
55     An enumeration of the color types supported by the png format.
56
57     see the $(LINK2 http://www.w3.org/TR/PNG/index-noobject.html#6Colour-values, png specification)
58     for details.
59
60 ***************************************************************************************************/
61 enum ColorType : ubyte
62 {
63     Greyscale = 0, /// allowed bit depths: 1, 2, 4, 8 and 16
64     RGB = 2, /// allowed bit depths: 8 and 16
65     Palette = 3, /// allowed bit depths: 1, 2, 4 and 8
66     GreyscaleAlpha = 4, /// allowed bit depths: 8 and 16
67     RGBA = 6, /// allowed bit depths: 8 and 16
68     Any = 7, /// one of the above
69 }
70
71 /***************************************************************************************************
72 Convert source pixels from the color type described by info the color type destColorType.
73
74     Conversion can be from any color type supported by the png specification to
75     24 / 32 bit RGB(A). If RGBA is specified and the info has a colorkey, transparency is applied.
76
77     If a buffer is given, it may be used to store the result and a slice from it can be returned.
78
79     Returns: converted image in RGB(A) format, pixels are from left to right, top to bottom
80 ***************************************************************************************************/
81 ubyte[] convert(in ubyte[] source, /+const+/ ref PngInfo info, ColorType destColorType = ColorType.RGBA)
82 {
83     ubyte[] buffer;
84     return convert(source, info, buffer, destColorType);
85 }
86
87 /// ditto
88 ubyte[] convert(in ubyte[] source, /+const+/in PngInfo info, ref ubyte[] buffer, ColorType destColorType = ColorType.RGBA)
89 {
90     if (!(destColorType == ColorType.RGBA || destColorType == ColorType.RGB))
91     {
92         destColorType = ColorType.RGBA;
93         assert(false, "destColorType should be one of: RGB, RGBA");
94     }
95     return _convert(source, info, buffer, destColorType);
96 }
97
98 /***************************************************************************************************
99     Description of the image.
100
101 ***************************************************************************************************/
102 struct PngImage
103 {
104     /// constructor
105     static PngImage opCall(uint w, uint h, ubyte bd, ColorType ct, ubyte ilace =0)
106     {
107         PngImage result;
108         with (result)
109         {
110             width = w;
111             height = h;
112             bitDepth = bd;
113             colorType = ct;
114             bpp = bitDepth * numChannels(colorType);
115             interlaced = ilace;
116         }
117         return result;
118     }
119     uint    width; /// in pixels
120     uint    height; /// in pixels
121     ubyte   bitDepth; /// bits per color channel, see also: ColorType
122     ubyte   bpp; /// bits per pixel
123     ColorType colorType; /// the color format, see also: ColorType
124     ubyte interlaced = 0;
125 }
126
127 /***************************************************************************************************
128 Png file and image description.
129
130     A simple data type describing the png file. Usually the image field will contain all the
131     required information.
132
133     There is one member that behaves a bit different: parseText(bool). This is used to tell
134     the decoder whether to ignore textual metadata (which it does by default).
135
136 ***************************************************************************************************/
137 struct PngInfo
138 {
139
140     PngImage image; /// Information related to the image, see also: PngImage.
141
142     /***********************************************************************************************
143     Recommended background color or empty if none is given.
144
145         Interpretation of the array depends on the color type:
146
147         $(DL
148             $(DT palette)
149                 $(DD palette[backgroundColor[0]] is the background color)
150             $(DT greyscale (8 bit or less) )
151                 $(DD backgroundColor[0] is used)
152             $(DT RGB(A) (8 bit or less))
153                 $(DD backgroundColor[0..3] is the rgb triplet used)
154             $(DT 16-bit greyscale or RGB(A))
155                 $(DD  same as above, but the color/greyscale channels are 2 bytes wide))
156
157     ***********************************************************************************************/
158     ubyte[] backgroundColor;
159
160     /***********************************************************************************************
161     Palette colors or empty if this image does not make use of it.
162
163           Each palette entry is a 32-bit RGBA pixel represented as an ubyte[4].
164           When a colorkey is specified, it is automatically applied to the palette by the decoder.
165
166     ***********************************************************************************************/
167     ubyte[4][] palette;
168
169     /***********************************************************************************************
170         Whether there is a colorkey transparency associated with the png image.
171
172             Note that this is applicable only when the image has no seperate alpha channel, and
173             the image format is not ColorType.Palette. In the latter case, transparency is always
174             stored in the palette by the decoder.
175             The transparent color is stored in keyR, keyG and keyB, for brevity greyscale is in keyR.
176     ***********************************************************************************************/
177     bool   colorKey = false;
178     ushort keyR;     ///
179     ushort keyG;     ///
180     ushort keyB;     ///
181
182     /// By default, lodepng will not parse textual metadata, set to true if this is desired.
183     void parseText(bool flag)
184     {
185         parseTextChunks = flag;
186     }
187
188     /// Whether parsing of metadata is enabled.
189     bool parseText()
190     {
191         return parseTextChunks;
192     }
193
194     /***********************************************************************************************
195         Retrieve the dictionary of textual metadata.
196
197             When no text is read, because it was set to be ignored or there wasn't any,
198             this returns null so check the return value.
199
200             In the case that a PngInfo instance is passed more than once to the api,
201             an existing dictionary is not reused. Instead a new one will be
202             created. It is therefore not necessary to copy anything, the strings they are all yours.
203
204     ***********************************************************************************************/
205     PngText text()
206     {
207         return textual;
208     }
209
210     /// Returns whether any unicode text has been stored.
211     bool hasUnicodeText()
212     {
213         return textual !is null && textual.unicodeText.length > 0;
214     }
215
216     /// Returns whether any latin-1 text has been stored.
217     bool hasLatin1Text()
218     {
219         return textual !is null && textual.latin1Text.length > 0;
220     }
221
222     package
223     {
224         ubyte interlace;
225         PngText textual;
226     }
227
228     char[][char[]] unicodeText;
229     ubyte[][ubyte[]] latin1Text;
230
231     private bool parseTextChunks = true;
232 }
233
234 /// Dictionary of key-value textual metadata in utf-8 and / or latin-1 encoding.
235 class PngText
236 {
237     ///
238     this()
239     {
240     }
241
242     ///
243     this(char[][char[]] unicodeText)
244     {
245         this.unicodeText = unicodeText;
246     }
247
248     ///
249     this(ubyte[][ubyte[]] latin1Text)
250     {
251         this.latin1Text = latin1Text;
252     }
253
254     /// Visit utf-8 dictionary
255     int opApply(int delegate(ref char[] keyword, ref char[] contents) dg)
256     {
257         int result = 0;
258         foreach(index, value; unicodeText)
259         {
260             result = dg(index, value);
261             if (result)
262                 return result;
263         }
264         return 0;
265     }
266
267     /// Visit latin-1 dictionary
268     int opApply(int delegate(ref ubyte[] keyword, ref ubyte[] contents) dg)
269     {
270         int result = 0;
271         foreach(index, value; latin1Text)
272         {
273             result = dg(index, value);
274             if (result)
275                 return result;
276         }
277         return 0;
278     }
279
280     /// Assign key-value pair
281     PngText opIndexAssign(ubyte[] value, ubyte[] keyword)
282     {
283         latin1Text[value] = keyword;
284         return this;
285     }
286
287     /// ditto
288     PngText opIndexAssign(char[] value, char[] keyword)
289     {
290         unicodeText[value] = keyword;
291         return this;
292     }
293
294     package
295     {
296         char[][char[]] unicodeText;
297         ubyte[][ubyte[]] latin1Text;
298     }
299 }
300
301 /// Number of color or alpha channels associated with this color type.
302 uint numChannels(ColorType colorType)
303 in
304 {
305     assert(colorType != ColorType.Any, "cannot determine number of channels with ColorType.Any");
306 }
307 body
308 {
309     return [ 0 : 1, 2 : 3, 3 : 1, 4 : 2, 6 : 4, 7 : 0] [colorType];
310 }
311
312 uint bytesPerPixel(ref PngImage image)
313 {
314     return image.bpp / 8;
315 }
316
317 ///
318 bool isGreyscale(ColorType colorType)
319 {
320     return colorType == ColorType.Greyscale || colorType == ColorType.GreyscaleAlpha;
321 }
322
323 ///
324 bool hasAlphaChannel(ColorType colorType)
325 {
326     return colorType == ColorType.RGBA || colorType == ColorType.GreyscaleAlpha;
327 }
328
329 /// Whether the image contains any alpha information (channel / colorkey / palette)
330 bool hasAlpha(/+const+/ ref PngInfo info)
331 {
332     if (hasAlphaChannel(info.image.colorType) || info.colorKey)
333         return true;
334     else if (info.image.colorType == ColorType.Palette)
335         foreach(color; info.palette)
336             if (color[3] != 255)
337                 return true;
338     return false;
339 }
340
341 bool checkCRC(uint crc, ubyte[] data)
342 {
343     return crc == czlib.crc32(0, cast(ubyte *)data, data.length);
344 }
345
346 bool checkCRC(ubyte[] crc, ubyte[] data)
347 {
348     return toUint(crc) == czlib.crc32(0, cast(ubyte *)data, data.length);
349 }
350
351 uint createCRC(ubyte[] data)
352 {
353     return czlib.crc32(0, cast(ubyte *)data, data.length);
354 }
355 /**
356  * Abstracts a png chunk
357  *
358  * */
359 struct Chunk
360 {
361     /**
362      * Construct a Chunk from a stream of bytes
363      *
364      * Params:
365      *     byteStream = the whole chunk needs to be available in this array
366      * Returns: a crc-checked chunk from byteStream, the data is sliced, not copied
367      */
368     static Chunk fromStream(ubyte[] byteStream)
369     in
370     {
371         assert(byteStream.length >= 12);
372     }
373     body
374     {
375         Chunk result;
376         uint dataLength = toUint(byteStream);
377         result.type = toUint(byteStream[4 .. 8]);
378         if (dataLength)
379             result.data = byteStream[8 .. 8 + dataLength];
380         mixin(pngEnforce("checkCRC(byteStream[8 + dataLength..12 + dataLength], byteStream[4..8 + dataLength])",
381                          "CRC check failed"));
382         return result;
383     }
384    
385     int opCmp(Chunk other)
386     {
387         int result = chunkOrder(type, other.type);
388
389         /* Setting of the afterIDAT member is not yet implemented.  It would
390          * be used to prevent unknown ancillary chunks from being moved from
391          * before the IDAT chunk to after it, or vice versa.
392
393         if (result == 0) {
394             result = cast(int) afterIDAT - cast(int) other.afterIDAT;
395         }
396         if (result == 0) {
397             // if one of them is IDAT, the other is before IDAT
398             if (type == IDAT) return 1;
399             if (other.type == IDAT) return -1;
400         }
401          */
402         return result;
403     }
404
405     /**
406      *
407      * Returns: total length of this chunk (not only data)
408      */
409     uint length() { return data.length + 12; }
410    
411     /**
412      *
413      * Returns: chunk containing a copy of the data
414      */
415     Chunk dup()
416     {
417         return Chunk(type, data.dup);
418        
419     }
420    
421     /**
422      * type of the chunk packed into a uint, see the png specification for details
423      */
424     uint type;
425    
426     /**
427      * (slice of) the data of this chunk
428      */
429     ubyte[] data;
430     //bool afterIDAT;
431 }
432
433 /**
434  *
435  * Params:
436  *     a =
437  *     b =
438  *     c =
439  *     d =
440  * Returns:
441  */
442 uint toUint(ubyte a, ubyte b, ubyte c, ubyte d) { return a << 24 | b << 16 | c << 8 | d; }
443
444 /**
445  *
446  * Params:
447  *     source =
448  * Returns:
449  */
450 uint toUint(ubyte[] source)
451     in { assert(source.length >= 4); }
452     body { return source[0] << 24 | source[1] << 16 | source[2] << 8 | source[3]; }
453
454 template toConstUint(char[] source, Dummy = void)
455 {
456     const uint toConstUint = source[0] << 24 | source[1] << 16 | source[2] << 8 | source[3];
457 }
458
459     const IHDR = toConstUint!("IHDR");///
460     //const IHDR = toUint('I', 'H', 'D', 'R');
461     const IDAT = toUint('I', 'D', 'A', 'T');///
462     const PLTE = toUint('P', 'L', 'T', 'E');///
463     const tRNS = toUint('t', 'R', 'N', 'S');///
464     const bKGD = toUint('b', 'K', 'G', 'D');///
465     const IEND = toUint('I', 'E', 'N', 'D');///
466     const tEXt = toUint('t', 'E', 'X', 't');///
467     const iTXt = toUint('i', 'T', 'X', 't');///
468     const zTXt = toUint('z', 'T', 'X', 't');///
469     const cHRM = toUint('c', 'H', 'R', 'M');///
470     const gAMA = toUint('g', 'A', 'M', 'A');///
471     const hIST = toUint('h', 'I', 'S', 'T');///
472     const iCCP = toUint('i', 'C', 'C', 'P');///
473     const oFFs = toUint('o', 'F', 'F', 's');///
474     const pCAL = toUint('p', 'C', 'A', 'L');///
475     const pHYs = toUint('p', 'H', 'Y', 's');///
476     const sBIT = toUint('s', 'B', 'I', 'T');///
477     const sCAL = toUint('s', 'C', 'A', 'L');///
478     const sPLT = toUint('s', 'P', 'L', 'T');///
479     const sRGB = toUint('s', 'R', 'G', 'B');///
480
481 package
482 {
483     //return type is a LodePNG error code
484     bool checkColorValidity(uint colorType, uint bitDepth)
485     {
486         alias bitDepth bd;
487         switch(colorType)
488         {
489             case 0: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) //grey
490                         return false;
491                 break;
492             case 2: if(!(bd == 8 || bd == 16)) //RGB
493                         return false;
494                 break;
495             case 3: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) //palette
496                         return false;
497                 break;
498             case 4: if(!(bd == 8 || bd == 16)) //greyalpha
499                         return false;
500                 break;
501             case 6: if(!(bd == 8 || bd == 16)) //RGBA
502                         return false;
503                 break;
504             default:
505                 mixin(pngEnforce(`false`, "invalid png color format"));
506         }
507         return true;
508     }
509
510     // Paeth predicter, used by one of the PNG filter types
511     int paethPredictor(int a, int b, int c)
512     {
513         int p = a + b - c;
514         int pa = p > a ? p - a : a - p;
515         int pb = p > b ? p - b : b - p;
516         int pc = p > c ? p - c : c - p;
517
518         if(pa <= pb && pa <= pc) return a;
519         else if(pb <= pc) return b;
520         return c;
521     }
522
523     uint readBitFromStreamReversed(ref size_t bitpointer, ubyte[] bitstream)
524     {
525         return (bitstream[bitpointer / 8] >> (7 - bitpointer++ & 0x7)) & 1;
526     }
527 }
528
529 private int chunkOrder(uint type1, uint type2) {
530     if (type1 == type2) return 0;
531
532     // sort out IHDR and IEND first
533     if (type1 == IHDR) return -1;
534     if (type1 == IEND) return 1;
535     if (type2 == IHDR) return 1;
536     if (type2 == IEND) return -1;
537
538     // now sort out PLTE and IDAT
539     switch (type1) {
540         case PLTE:
541             switch (type2) {
542                 case gAMA: case cHRM: case sRGB: case iCCP: case sBIT:
543                     return 1;
544
545                 case tRNS: case bKGD: case hIST: case IDAT:
546                     return -1;
547
548                 default:
549                     return 0;
550             }
551
552         case IDAT:
553             switch (type2) {
554                 case gAMA: case cHRM: case sRGB: case iCCP: case sBIT:
555                 case PLTE: case tRNS: case bKGD: case hIST: case pHYs:
556                 case sPLT: case oFFs: case pCAL: case sCAL:
557                     return 1;
558
559                 default:
560                     return 0;
561             }
562
563         default:
564             if (type2 == PLTE || type2 == IDAT) {
565                 return -chunkOrder(type2, type1);
566             }
567     }
568
569     // if we reach this point, they're both ancillary chunks
570     switch (type1) {
571         case gAMA: case cHRM: case sRGB: case iCCP: case sBIT:
572             switch (type2) {
573                 case tRNS: case bKGD: case hIST: case IDAT:
574                     return -1;
575
576                 default:
577                     return 0;
578             }
579
580         case tRNS: case bKGD: case hIST: case IDAT:
581             switch (type2) {
582                 case gAMA: case cHRM: case sRGB: case iCCP: case sBIT:
583                     return 1;
584
585                 default:
586                     return 0;
587             }
588
589         default:
590             return 0;
591     }
592 }
593
594
595
596
597 private ubyte[] _convert(in ubyte[] source, /+const+/ ref PngInfo info, ref ubyte[] buffer, ColorType destColorType)
598 {
599     bool alpha = hasAlphaChannel(destColorType);
600
601     if (destColorType == info.image.colorType)
602     {
603         if ((destColorType == ColorType.RGBA || destColorType == ColorType.RGB) && info.image.bitDepth == 8)
604         {
605             buffer = source;
606             return buffer;
607         }
608     }
609
610
611     uint w = info.image.width;
612     uint h = info.image.height;
613     ubyte[] res = buffer;
614     ubyte colors = alpha ? 4 : 3;
615
616     res.length = w * h * colors;
617
618     assert(source.length >=  (w * (info.image.bpp / 8  ) * h));
619
620      if (!info.colorKey && alpha)
621         for(size_t i = 0; i < w * h; i++)
622             res[4 * i + 3] = 255;
623
624     switch(info.image.colorType)
625     {
626         case 0: //greyscale color
627         {
628             if(info.image.bitDepth == 8)
629             {
630                 foreach(i, pixel; source[0 .. w * h])
631                     res[colors * i + 0] = res[colors * i + 1] = res[colors * i + 2] = pixel;
632                 if (info.colorKey && alpha)
633                     foreach(i, pixel; source[0 .. w * h])
634                         res[colors * i + 3] = (pixel == info.keyR) ? 0 : 255;
635             }
636             else if (info.image.bitDepth == 8)
637             {
638