xref: /aosp_15_r20/external/zopfli/src/zopflipng/lodepng/lodepng_util.cpp (revision e47783fd9ac7e78d0523d35be12ee382df490d63)
1 /*
2 LodePNG Utils
3 
4 Copyright (c) 2005-2020 Lode Vandevenne
5 
6 This software is provided 'as-is', without any express or implied
7 warranty. In no event will the authors be held liable for any damages
8 arising from the use of this software.
9 
10 Permission is granted to anyone to use this software for any purpose,
11 including commercial applications, and to alter it and redistribute it
12 freely, subject to the following restrictions:
13 
14     1. The origin of this software must not be misrepresented; you must not
15     claim that you wrote the original software. If you use this software
16     in a product, an acknowledgment in the product documentation would be
17     appreciated but is not required.
18 
19     2. Altered source versions must be plainly marked as such, and must not be
20     misrepresented as being the original software.
21 
22     3. This notice may not be removed or altered from any source
23     distribution.
24 */
25 
26 #include "lodepng_util.h"
27 #include <iostream>  // TODO: remove, don't print stuff from here, return errors instead
28 #include <stdlib.h> /* allocations */
29 
30 namespace lodepng {
31 
getPNGHeaderInfo(const std::vector<unsigned char> & png)32 LodePNGInfo getPNGHeaderInfo(const std::vector<unsigned char>& png) {
33   unsigned w, h;
34   lodepng::State state;
35   lodepng_inspect(&w, &h, &state, &png[0], png.size());
36   return state.info_png;
37 }
38 
getChunkInfo(std::vector<std::string> & names,std::vector<size_t> & sizes,const std::vector<unsigned char> & png)39 unsigned getChunkInfo(std::vector<std::string>& names, std::vector<size_t>& sizes,
40                       const std::vector<unsigned char>& png) {
41   // Listing chunks is based on the original file, not the decoded png info.
42   const unsigned char *chunk, *end;
43   end = &png.back() + 1;
44   chunk = &png.front() + 8;
45 
46   while(chunk < end && end - chunk >= 8) {
47     char type[5];
48     lodepng_chunk_type(type, chunk);
49     if(std::string(type).size() != 4) return 1;
50 
51     unsigned length = lodepng_chunk_length(chunk);
52     names.push_back(type);
53     sizes.push_back(length);
54     chunk = lodepng_chunk_next_const(chunk, end);
55   }
56   return 0;
57 }
58 
getChunks(std::vector<std::string> names[3],std::vector<std::vector<unsigned char>> chunks[3],const std::vector<unsigned char> & png)59 unsigned getChunks(std::vector<std::string> names[3],
60                    std::vector<std::vector<unsigned char> > chunks[3],
61                    const std::vector<unsigned char>& png) {
62   const unsigned char *chunk, *next, *end;
63   end = &png.back() + 1;
64   chunk = &png.front() + 8;
65 
66   int location = 0;
67 
68   while(chunk < end && end - chunk >= 8) {
69     char type[5];
70     lodepng_chunk_type(type, chunk);
71     std::string name(type);
72     if(name.size() != 4) return 1;
73 
74     next = lodepng_chunk_next_const(chunk, end);
75 
76     if(name == "IHDR") {
77       location = 0;
78     } else if(name == "PLTE") {
79       location = 1;
80     } else if(name == "IDAT") {
81       location = 2;
82     } else if(name == "IEND") {
83       break; // anything after IEND is not part of the PNG or the 3 groups here.
84     } else {
85       if(next >= end) return 1; // invalid chunk, content too far
86       names[location].push_back(name);
87       chunks[location].push_back(std::vector<unsigned char>(chunk, next));
88     }
89 
90     chunk = next;
91   }
92   return 0;
93 }
94 
95 
insertChunks(std::vector<unsigned char> & png,const std::vector<std::vector<unsigned char>> chunks[3])96 unsigned insertChunks(std::vector<unsigned char>& png,
97                       const std::vector<std::vector<unsigned char> > chunks[3]) {
98   const unsigned char *chunk, *begin, *end;
99   end = &png.back() + 1;
100   begin = chunk = &png.front() + 8;
101 
102   long l0 = 0; //location 0: IHDR-l0-PLTE (or IHDR-l0-l1-IDAT)
103   long l1 = 0; //location 1: PLTE-l1-IDAT (or IHDR-l0-l1-IDAT)
104   long l2 = 0; //location 2: IDAT-l2-IEND
105 
106   while(chunk < end && end - chunk >= 8) {
107     char type[5];
108     lodepng_chunk_type(type, chunk);
109     std::string name(type);
110     if(name.size() != 4) return 1;
111 
112     if(name == "PLTE") {
113       if(l0 == 0) l0 = chunk - begin + 8;
114     } else if(name == "IDAT") {
115       if(l0 == 0) l0 = chunk - begin + 8;
116       if(l1 == 0) l1 = chunk - begin + 8;
117     } else if(name == "IEND") {
118       if(l2 == 0) l2 = chunk - begin + 8;
119     }
120 
121     chunk = lodepng_chunk_next_const(chunk, end);
122   }
123 
124   std::vector<unsigned char> result;
125   result.insert(result.end(), png.begin(), png.begin() + l0);
126   for(size_t i = 0; i < chunks[0].size(); i++) result.insert(result.end(), chunks[0][i].begin(), chunks[0][i].end());
127   result.insert(result.end(), png.begin() + l0, png.begin() + l1);
128   for(size_t i = 0; i < chunks[1].size(); i++) result.insert(result.end(), chunks[1][i].begin(), chunks[1][i].end());
129   result.insert(result.end(), png.begin() + l1, png.begin() + l2);
130   for(size_t i = 0; i < chunks[2].size(); i++) result.insert(result.end(), chunks[2][i].begin(), chunks[2][i].end());
131   result.insert(result.end(), png.begin() + l2, png.end());
132 
133   png = result;
134   return 0;
135 }
136 
getFilterTypesInterlaced(std::vector<std::vector<unsigned char>> & filterTypes,const std::vector<unsigned char> & png)137 unsigned getFilterTypesInterlaced(std::vector<std::vector<unsigned char> >& filterTypes,
138                                   const std::vector<unsigned char>& png) {
139   //Get color type and interlace type
140   lodepng::State state;
141   unsigned w, h;
142   unsigned error;
143   error = lodepng_inspect(&w, &h, &state, &png[0], png.size());
144 
145   if(error) return 1;
146 
147   //Read literal data from all IDAT chunks
148   const unsigned char *chunk, *begin, *end;
149   end = &png.back() + 1;
150   begin = chunk = &png.front() + 8;
151 
152   std::vector<unsigned char> zdata;
153 
154   while(chunk < end && end - chunk >= 8) {
155     char type[5];
156     lodepng_chunk_type(type, chunk);
157     if(std::string(type).size() != 4) break; //Probably not a PNG file
158 
159     if(std::string(type) == "IDAT") {
160       const unsigned char* cdata = lodepng_chunk_data_const(chunk);
161       unsigned clength = lodepng_chunk_length(chunk);
162       if(chunk + clength + 12 > end || clength > png.size() || chunk + clength + 12 < begin) {
163         // corrupt chunk length
164         return 1;
165       }
166 
167       for(unsigned i = 0; i < clength; i++) {
168         zdata.push_back(cdata[i]);
169       }
170     }
171 
172     chunk = lodepng_chunk_next_const(chunk, end);
173   }
174 
175   //Decompress all IDAT data (if the while loop ended early, this might fail)
176   std::vector<unsigned char> data;
177   error = lodepng::decompress(data, &zdata[0], zdata.size());
178 
179   if(error) return 1;
180 
181   if(state.info_png.interlace_method == 0) {
182     filterTypes.resize(1);
183 
184     //A line is 1 filter byte + all pixels
185     size_t linebytes = 1 + lodepng_get_raw_size(w, 1, &state.info_png.color);
186 
187     for(size_t i = 0; i < data.size(); i += linebytes) {
188       filterTypes[0].push_back(data[i]);
189     }
190   } else {
191     //Interlaced
192     filterTypes.resize(7);
193     static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
194     static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
195     static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
196     static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
197     size_t pos = 0;
198     for(size_t j = 0; j < 7; j++) {
199       unsigned w2 = (w - ADAM7_IX[j] + ADAM7_DX[j] - 1) / ADAM7_DX[j];
200       unsigned h2 = (h - ADAM7_IY[j] + ADAM7_DY[j] - 1) / ADAM7_DY[j];
201       if(ADAM7_IX[j] >= w || ADAM7_IY[j] >= h) continue;
202       size_t linebytes = 1 + lodepng_get_raw_size(w2, 1, &state.info_png.color);
203       for(size_t i = 0; i < h2; i++) {
204         filterTypes[j].push_back(data[pos]);
205         pos += linebytes;
206       }
207     }
208   }
209   return 0; /* OK */
210 }
211 
212 
getFilterTypes(std::vector<unsigned char> & filterTypes,const std::vector<unsigned char> & png)213 unsigned getFilterTypes(std::vector<unsigned char>& filterTypes, const std::vector<unsigned char>& png) {
214   std::vector<std::vector<unsigned char> > passes;
215   unsigned error = getFilterTypesInterlaced(passes, png);
216   if(error) return error;
217 
218   if(passes.size() == 1) {
219     filterTypes.swap(passes[0]);
220   } else {
221     // Simplify interlaced filter types to get a single filter value per scanline:
222     // put pass 6 and 7 alternating in the one vector, these filters
223     // correspond to the closest to what it would be for non-interlaced
224     // image. If the image is only 1 pixel wide, pass 6 doesn't exist so the
225     // alternative values column0 are used. The shift values are to match
226     // the y position in the interlaced sub-images.
227     // NOTE: the values 0-6 match Adam7's passes 1-7.
228     const unsigned column0[8] = {0, 6, 4, 6, 2, 6, 4, 6};
229     const unsigned column1[8] = {5, 6, 5, 6, 5, 6, 5, 6};
230     const unsigned shift0[8] = {3, 1, 2, 1, 3, 1, 2, 1};
231     const unsigned shift1[8] = {1, 1, 1, 1, 1, 1, 1, 1};
232     lodepng::State state;
233     unsigned w, h;
234     lodepng_inspect(&w, &h, &state, &png[0], png.size());
235     const unsigned* column = w > 1 ? column1 : column0;
236     const unsigned* shift = w > 1 ? shift1 : shift0;
237     for(size_t i = 0; i < h; i++) {
238       filterTypes.push_back(passes[column[i & 7u]][i >> shift[i & 7u]]);
239     }
240   }
241   return 0; /* OK */
242 }
243 
getPaletteValue(const unsigned char * data,size_t i,int bits)244 int getPaletteValue(const unsigned char* data, size_t i, int bits) {
245   if(bits == 8) return data[i];
246   else if(bits == 4) return (data[i / 2] >> ((i % 2) * 4)) & 15;
247   else if(bits == 2) return (data[i / 4] >> ((i % 4) * 2)) & 3;
248   else if(bits == 1) return (data[i / 8] >> (i % 8)) & 1;
249   else return 0;
250 }
251 
252 
253 ////////////////////////////////////////////////////////////////////////////////
254 
255 #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
256 
257 
258 
259 // Only temporarily here until this is integrated into lodepng.c(pp)
260 #define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b))
261 #define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b))
262 
263 // Only temporarily here until this is integrated into lodepng.c(pp)
264 #ifdef LODEPNG_COMPILE_ALLOCATORS
lodepng_malloc(size_t size)265 static void* lodepng_malloc(size_t size) {
266   return malloc(size);
267 }
lodepng_free(void * ptr)268 static void lodepng_free(void* ptr) {
269   free(ptr);
270 }
271 #else /*LODEPNG_COMPILE_ALLOCATORS*/
272 void* lodepng_malloc(size_t size);
273 void lodepng_free(void* ptr);
274 #endif /*LODEPNG_COMPILE_ALLOCATORS*/
275 
276 /* avoid needing <float.h> for FLT_MAX. This assumes IEEE 32-bit float. */
277 static const float lodepng_flt_max = 3.40282346638528859811704183484516925e38f;
278 
279 /* define infinity and NaN in a way compatible with ANSI C90 (no INFINITY or NAN macros) yet also with visual studio */
280 /* visual studio doesn't allow division through a zero literal, but allows it through non-const variable set to zero */
281 float lodepng_flt_zero_ = 0.0f;
282 static const float lodepng_flt_inf = 1.0f / lodepng_flt_zero_; /* infinity */
283 static const float lodepng_flt_nan = 0.0f / lodepng_flt_zero_; /* not a number */
284 
285 
286 /* powf polyfill, 5-6 digits accurate, 33-80% slower than powf, assumes IEEE
287 32-bit float, but other than that multiplatform and no math lib needed
288 (note: powf also isn't in ISO C90, and pow is slower). */
lodepng_powf(float x,float y)289 static float lodepng_powf(float x, float y) {
290   float j, t0, t1, l;
291   int i = 0;
292   /* handle all the special floating point rules */
293   if(x == 1 || y == 0) return 1; /*these cases return 1 even if the other value is NaN, as specified*/
294   if(y == 1) return x;
295   if(!(x > 0 && x <= lodepng_flt_max && y == y && y <= lodepng_flt_max && y >= -lodepng_flt_max)) {
296     if(y == 1) return x; /* preserves negative-0 */
297     if(x != x || y != y) return x + y; /* nan */
298     if(x > 0) {
299       if(x > lodepng_flt_max) return y <= 0 ? (y == 0 ? 1 : 0) : x; /* x = +infinity */
300     } else {
301       if(!(y < -1073741824.0f || y > 1073741824.0f)) { /* large y always even integer, but cast would overflow */
302         i = (int)y;
303         if(i != y) {
304           return (x < -lodepng_flt_max) ? (y < 0 ? 0 : lodepng_flt_inf) :
305               (x == 0 ? (y < 0 ? lodepng_flt_inf : 0) : lodepng_flt_nan);
306         }
307         if(i & 1) return x == 0 ? (y < 0 ? (1 / x) : x) : -lodepng_powf(-x, y);
308       }
309       if(x == 0) return y <= 0 ? lodepng_flt_inf : 0;
310       if(x < -lodepng_flt_max) { /* x == -infinity */
311         return y <= 0 ? (y == 0 ? 1 : 0) : ((i & 1) ?
312             -lodepng_flt_inf : lodepng_flt_inf);
313       }
314       x = -x;
315       if(x == 1) return 1;
316     }
317     if(y < -lodepng_flt_max || y > lodepng_flt_max) return ((x < 1) != (y > 0)) ? (y < 0 ? -y : y) : 0;
318   }
319 
320   l = x;
321   j = 0;
322   while(l < (1.0f / 65536)) { j -= 16; l *= 65536.0f; }
323   while(l > 65536) { j += 16; l *= (1.0f / 65536); }
324   while(l < 1) { j--; l *= 2.0f; }
325   while(l > 2) { j++; l *= 0.5f; }
326   /* polynomial to approximate log2(x) with x in range 1..2 */
327   t0 = -0.393118410458557f + l * (-0.0883639468229365f + l * (0.466142650227994f + l * 0.0153397331014276f));
328   t1 = 0.0907447971403586f + l * (0.388892024755479f + l * 0.137228280305862f);
329   l = t0 / t1 + j;
330 
331   l *= y; /* using the formula exp2(y * log2(x)) */
332 
333   /* prevent int shift overflow, 0 or inf result are ok to return since exp will be taken, 127 is max float exponent */
334   if(l <= -128.0f || l >= 128.0f) return ((x > 1) == (y > 0)) ? lodepng_flt_inf : 0;
335   i = (int)l;
336   l -= i;
337   /* polynomial to approximate exp2(x) with x in range -1..1 */
338   t0 = 1.0f + l * (0.41777833582744256f + l * (0.0728482595347711f + l * 0.005635023478609625f));
339   t1 = 1.0f + l * (-0.27537016151408167f + l * 0.023501446055084033f);
340   while(i <= -31) { t0 *= (1.0f / 2147483648.0f); i += 31; }
341   while(i >= 31) { t0 *= 2147483648.0f; i -= 31; }
342   return (i < 0) ? (t0 / (t1 * (1 << -i))) : ((t0 * (1 << i)) / t1);
343 }
344 
345 /* Parameters of a tone reproduction curve, either with a power law formula or with a lookup table. */
346 typedef struct {
347   unsigned type; /* 0=linear, 1=lut, 2 = simple gamma, 3-6 = parametric (matches ICC parametric types 1-4) */
348   float* lut; /* for type 1 */
349   size_t lut_size;
350   float gamma; /* for type 2 and more */
351   float a, b, c, d, e, f; /* parameters for type 3-7 */
352 } LodePNGICCCurve;
353 
lodepng_icc_curve_init(LodePNGICCCurve * curve)354 void lodepng_icc_curve_init(LodePNGICCCurve* curve) {
355   curve->lut = 0;
356   curve->lut_size = 0;
357 }
358 
lodepng_icc_curve_cleanup(LodePNGICCCurve * curve)359 void lodepng_icc_curve_cleanup(LodePNGICCCurve* curve) {
360   lodepng_free(curve->lut);
361 }
362 
363 /* Values parsed from ICC profile, see parseICC for more information about this subset.*/
364 typedef struct {
365   /* 0 = color model not supported by PNG (CMYK, Lab, ...), 1 = gray, 2 = RGB */
366   int inputspace;
367   int version_major;
368   int version_minor;
369   int version_bugfix;
370 
371   /* The whitepoint of the profile connection space (PCS). Should always be D50, but parsed and used anyway.
372   (to be clear, whitepoint and illuminant are synonyms in practice, but here field "illuminant" is ICC's
373   "global" whitepoint that is always D50, and the field "white" below allows deriving the whitepoint of
374   the particular RGB space represented here) */
375   float illuminant[3];
376 
377   /* if true, has chromatic adaptation matrix that must be used. If false, you must compute a chromatic adaptation
378   matrix yourself from "illuminant" and "white". */
379   unsigned has_chad;
380   float chad[9]; /* chromatic adaptation matrix, if given */
381 
382   /* The whitepoint of the RGB color space as stored in the ICC file. If has_chad, must be adapted with the
383   chad matrix to become the one we need to go to absolute XYZ (in fact ICC implies it should then be
384   exactly D50 in the file, redundantly, before this transformation with chad), else use as-is (then its
385   values can actually be something else than D50, and are the ones we need). */
386   unsigned has_whitepoint;
387   float white[3];
388   /* Chromaticities of the RGB space in XYZ color space, but given such that you must still
389   whitepoint adapt them from D50 to the RGB space whitepoint to go to absolute XYZ (if has_chad,
390   with chad, else with bradford adaptation matrix from illuminant to white). */
391   unsigned has_chromaticity;
392   float red[3];
393   float green[3];
394   float blue[3];
395 
396   unsigned has_trc; /* TRC = tone reproduction curve (aka "gamma correction") */
397 
398   /* TRC's for the three channels (only first one used if grayscale) */
399   LodePNGICCCurve trc[3];
400 } LodePNGICC;
401 
lodepng_icc_init(LodePNGICC * icc)402 void lodepng_icc_init(LodePNGICC* icc) {
403   lodepng_icc_curve_init(&icc->trc[0]);
404   lodepng_icc_curve_init(&icc->trc[1]);
405   lodepng_icc_curve_init(&icc->trc[2]);
406 }
407 
lodepng_icc_cleanup(LodePNGICC * icc)408 void lodepng_icc_cleanup(LodePNGICC* icc) {
409   lodepng_icc_curve_cleanup(&icc->trc[0]);
410   lodepng_icc_curve_cleanup(&icc->trc[1]);
411   lodepng_icc_curve_cleanup(&icc->trc[2]);
412 }
413 
414 /* ICC tone response curve, nonlinear (encoded) to linear.
415 Input and output in range 0-1. If color was integer 0-255, multiply with (1.0f/255)
416 to get the correct floating point behavior.
417 Outside of range 0-1, will not clip but either return x itself, or in cases
418 where it makes sense, a value defined by the same function.
419 NOTE: ICC requires clipping, but we do that only later when converting float to integer.*/
iccForwardTRC(const LodePNGICCCurve * curve,float x)420 static float iccForwardTRC(const LodePNGICCCurve* curve, float x) {
421   if(curve->type == 0) {
422     return x;
423   }
424   if(curve->type == 1) { /* Lookup table */
425     float v0, v1, fraction;
426     size_t index;
427     if(!curve->lut) return 0; /* error */
428     if(x < 0) return x;
429     index = (size_t)(x * (curve->lut_size - 1));
430     if(index >= curve->lut_size) return x;
431 
432     /* LERP */
433     v0 = curve->lut[index];
434     v1 = (index + 1 < curve->lut_size) ? curve->lut[index + 1] : 1.0f;
435     fraction = (x * (curve->lut_size - 1)) - index;
436     return v0 * (1 - fraction) + v1 * fraction;
437   }
438   if(curve->type == 2) {
439     /* Gamma expansion */
440     return (x > 0) ? lodepng_powf(x, curve->gamma) : x;
441   }
442   /* TODO: all the ones below are untested */
443   if(curve->type == 3) {
444     if(x < 0) return x;
445     return x >= (-curve->b / curve->a) ? (lodepng_powf(curve->a * x + curve->b, curve->gamma) + curve->c) : 0;
446   }
447   if(curve->type == 4) {
448     if(x < 0) return x;
449     return x >= (-curve->b / curve->a) ? (lodepng_powf(curve->a * x + curve->b, curve->gamma) + curve->c) : curve->c;
450   }
451   if(curve->type == 5) {
452     return x >= curve->d ? (lodepng_powf(curve->a * x + curve->b, curve->gamma)) : (curve->c * x);
453   }
454   if(curve->type == 6) {
455     return x >= curve->d ? (lodepng_powf(curve->a * x + curve->b, curve->gamma) + curve->c) : (curve->c * x + curve->f);
456   }
457   return 0;
458 }
459 
460 /* ICC tone response curve, linear to nonlinear (encoded).
461 Input and output in range 0-1. Outside of that range, will not clip but either
462 return x itself, or in cases where it makes sense, a value defined by the same function.
463 NOTE: ICC requires clipping, but we do that only later when converting float to integer.*/
iccBackwardTRC(const LodePNGICCCurve * curve,float x)464 static float iccBackwardTRC(const LodePNGICCCurve* curve, float x) {
465   if(curve->type == 0) {
466     return x;
467   }
468   if(curve->type == 1) {
469     size_t a, b, m;
470     float v;
471     if(x <= 0) return x;
472     if(x >= 1) return x;
473     /* binary search in the table */
474     /* TODO: use faster way of inverting the lookup table */
475     a = 0;
476     b = curve->lut_size;
477     for(;;) {
478       if(a == b) return curve->lut[a];
479       if(a + 1 == b) {
480         /* LERP */
481         float v0 = curve->lut[a];
482         float v1 = curve->lut[b];
483         float fraction;
484         if(v0 == v1) return v0;
485         fraction = (x - v0) / (v1 - v0);
486         return v0 * (1 - fraction) + v1 * fraction;
487       }
488       m = (a + b) / 2u;
489       v = curve->lut[m];
490       if(v > x) {
491         b = m;
492       } else {
493         a = m;
494       }
495     }
496     return 0;
497   }
498   if(curve->type == 2) {
499     /* Gamma compression */
500     return (x > 0) ? lodepng_powf(x, 1.0f / curve->gamma) : x;
501   }
502   /* TODO: all the ones below are untested  */
503   if(curve->type == 3) {
504     if(x < 0) return x;
505     return x > 0 ? ((lodepng_powf(x, 1.0f / curve->gamma) - curve->b) / curve->a) : (-curve->b / curve->a);
506   }
507   if(curve->type == 4) {
508     if(x < 0) return x;
509     return x > curve->c ?
510         ((lodepng_powf(x - curve->c, 1.0f / curve->gamma) - curve->b) / curve->a) :
511         (-curve->b / curve->a);
512   }
513   if(curve->type == 5) {
514     return x > (curve->c * curve->d) ?
515         ((lodepng_powf(x, 1.0f / curve->gamma) - curve->b) / curve->a) :
516         (x / curve->c);
517   }
518   if(curve->type == 6) {
519     return x > (curve->c * curve->d + curve->f) ?
520         ((lodepng_powf(x - curve->c, 1.0f / curve->gamma) - curve->b) / curve->a) :
521         ((x - curve->f) / curve->c);
522   }
523   return 0;
524 }
525 
decodeICCUint16(const unsigned char * data,size_t size,size_t * pos)526 static unsigned decodeICCUint16(const unsigned char* data, size_t size, size_t* pos) {
527   *pos += 2;
528   if (*pos > size) return 0;
529   return (unsigned)((data[*pos - 2] << 8) | (data[*pos - 1]));
530 }
531 
decodeICCUint32(const unsigned char * data,size_t size,size_t * pos)532 static unsigned decodeICCUint32(const unsigned char* data, size_t size, size_t* pos) {
533   *pos += 4;
534   if (*pos > size) return 0;
535   return (unsigned)((data[*pos - 4] << 24) | (data[*pos - 3] << 16) | (data[*pos - 2] << 8) | (data[*pos - 1] << 0));
536 }
537 
decodeICCInt32(const unsigned char * data,size_t size,size_t * pos)538 static int decodeICCInt32(const unsigned char* data, size_t size, size_t* pos) {
539   *pos += 4;
540   if (*pos > size) return 0;
541   /*TODO: this is incorrect if sizeof(int) != 4*/
542   return (data[*pos - 4] << 24) | (data[*pos - 3] << 16) | (data[*pos - 2] << 8) | (data[*pos - 1] << 0);
543 }
544 
decodeICC15Fixed16(const unsigned char * data,size_t size,size_t * pos)545 static float decodeICC15Fixed16(const unsigned char* data, size_t size, size_t* pos) {
546   return decodeICCInt32(data, size, pos) / 65536.0;
547 }
548 
isICCword(const unsigned char * data,size_t size,size_t pos,const char * word)549 static unsigned isICCword(const unsigned char* data, size_t size, size_t pos, const char* word) {
550   if(pos + 4 > size) return 0;
551   return data[pos + 0] == (unsigned char)word[0] &&
552          data[pos + 1] == (unsigned char)word[1] &&
553          data[pos + 2] == (unsigned char)word[2] &&
554          data[pos + 3] == (unsigned char)word[3];
555 }
556 
557 /* Parses a subset of the ICC profile, supporting the necessary mix of ICC v2
558 and ICC v4 required to correctly convert the RGB color space to XYZ.
559 Does not parse values not related to this specific PNG-related purpose, and
560 does not support non-RGB profiles or lookup-table based chroma (but it
561 supports lookup tables for TRC aka "gamma"). */
parseICC(LodePNGICC * icc,const unsigned char * data,size_t size)562 static unsigned parseICC(LodePNGICC* icc, const unsigned char* data, size_t size) {
563   size_t i, j;
564   size_t pos = 0;
565   unsigned version;
566   unsigned inputspace;
567   size_t numtags;
568 
569   if(size < 132) return 1; /* Too small to be a valid icc profile. */
570 
571   icc->has_chromaticity = 0;
572   icc->has_whitepoint = 0;
573   icc->has_trc = 0;
574   icc->has_chad = 0;
575 
576   icc->trc[0].type = icc->trc[1].type = icc->trc[2].type = 0;
577   icc->white[0] = icc->white[1] = icc->white[2] = 0;
578   icc->red[0] = icc->red[1] = icc->red[2] = 0;
579   icc->green[0] = icc->green[1] = icc->green[2] = 0;
580   icc->blue[0] = icc->blue[1] = icc->blue[2] = 0;
581 
582   pos = 8;
583   version = decodeICCUint32(data, size, &pos);
584   if(pos >= size) return 1;
585   icc->version_major = (int)((version >> 24) & 255);
586   icc->version_minor = (int)((version >> 20) & 15);
587   icc->version_bugfix = (int)((version >> 16) & 15);
588 
589   pos = 16;
590   inputspace = decodeICCUint32(data, size, &pos);
591   if(pos >= size) return 1;
592   if(inputspace == 0x47524159) {
593     /* The string  "GRAY" as unsigned 32-bit int. */
594     icc->inputspace = 1;
595   } else if(inputspace == 0x52474220) {
596     /* The string  "RGB " as unsigned 32-bit int. */
597     icc->inputspace = 2;
598   } else {
599     /* unsupported by PNG (CMYK, YCbCr, Lab, HSV, ...) */
600     icc->inputspace = 0;
601   }
602 
603   /* Should always be 0.9642, 1.0, 0.8249 */
604   pos = 68;
605   icc->illuminant[0] = decodeICC15Fixed16(data, size, &pos);
606   icc->illuminant[1] = decodeICC15Fixed16(data, size, &pos);
607   icc->illuminant[2] = decodeICC15Fixed16(data, size, &pos);
608 
609   pos = 128;
610   numtags = decodeICCUint32(data, size, &pos);
611   if(pos >= size) return 1;
612   /* scan for tags we want to handle */
613   for(i = 0; i < numtags; i++) {
614     size_t offset;
615     unsigned tagsize;
616     size_t namepos = pos;
617     pos += 4;
618     offset = decodeICCUint32(data, size, &pos);
619     tagsize = decodeICCUint32(data, size, &pos);
620     if(pos >= size || offset >= size) return 1;
621     if(offset + tagsize > size) return 1;
622     if(tagsize < 8) return 1;
623 
624     if(isICCword(data, size, namepos, "wtpt")) {
625       offset += 8; /* skip tag and reserved */
626       icc->white[0] = decodeICC15Fixed16(data, size, &offset);
627       icc->white[1] = decodeICC15Fixed16(data, size, &offset);
628       icc->white[2] = decodeICC15Fixed16(data, size, &offset);
629       icc->has_whitepoint = 1;
630     } else if(isICCword(data, size, namepos, "rXYZ")) {
631       offset += 8; /* skip tag and reserved */
632       icc->red[0] = decodeICC15Fixed16(data, size, &offset);
633       icc->red[1] = decodeICC15Fixed16(data, size, &offset);
634       icc->red[2] = decodeICC15Fixed16(data, size, &offset);
635       icc->has_chromaticity = 1;
636     } else if(isICCword(data, size, namepos, "gXYZ")) {
637       offset += 8; /* skip tag and reserved */
638       icc->green[0] = decodeICC15Fixed16(data, size, &offset);
639       icc->green[1] = decodeICC15Fixed16(data, size, &offset);
640       icc->green[2] = decodeICC15Fixed16(data, size, &offset);
641       icc->has_chromaticity = 1;
642     } else if(isICCword(data, size, namepos, "bXYZ")) {
643       offset += 8; /* skip tag and reserved */
644       icc->blue[0] = decodeICC15Fixed16(data, size, &offset);
645       icc->blue[1] = decodeICC15Fixed16(data, size, &offset);
646       icc->blue[2] = decodeICC15Fixed16(data, size, &offset);
647       icc->has_chromaticity = 1;
648     } else if(isICCword(data, size, namepos, "chad")) {
649       offset += 8; /* skip datatype keyword "sf32" and reserved */
650       for(j = 0; j < 9; j++) {
651         icc->chad[j] = decodeICC15Fixed16(data, size, &offset);
652       }
653       icc->has_chad = 1;
654     } else if(isICCword(data, size, namepos, "rTRC") ||
655               isICCword(data, size, namepos, "gTRC") ||
656               isICCword(data, size, namepos, "bTRC") ||
657               isICCword(data, size, namepos, "kTRC")) {
658       char c = (char)data[namepos];
659       /* both 'k' and 'r' are stored in channel 0 */
660       int channel = (c == 'b') ? 2 : (c == 'g' ? 1 : 0);
661       /* "curv": linear, gamma power or LUT */
662       if(isICCword(data, size, offset, "curv")) {
663         size_t count;
664         LodePNGICCCurve* trc = &icc->trc[channel];
665         icc->has_trc = 1;
666         offset += 8; /* skip tag "curv" and reserved */
667         count = decodeICCUint32(data, size, &offset);
668         if(count == 0) {
669           trc->type = 0; /* linear */
670         } else if(count == 1) {
671           trc->type = 2; /* gamma */
672           trc->gamma = decodeICCUint16(data, size, &offset) / 256.0f;
673         } else {
674           trc->type = 1; /* LUT */
675           if(offset + count * 2 > size || count > 16777216) return 1; /* also avoid crazy count */
676           trc->lut_size = count;
677           trc->lut = (float*)lodepng_malloc(count * sizeof(float));
678           for(j = 0; j < count; j++) {
679             trc->lut[j] = decodeICCUint16(data, size, &offset) * (1.0f / 65535.0f);
680           }
681         }
682       }
683       /* "para": parametric formula with gamma power, multipliers, biases and comparison point */
684       /* TODO: test this on a realistic sample */
685       if(isICCword(data, size, offset, "para")) {
686         unsigned type;
687         LodePNGICCCurve* trc = &icc->trc[channel];
688         icc->has_trc = 1;
689         offset += 8; /* skip tag "para" and reserved */
690         type = decodeICCUint16(data, size, &offset);
691         offset += 2;
692         if(type > 4) return 1; /* unknown parametric curve type */
693         trc->type = type + 2;
694         trc->gamma = decodeICC15Fixed16(data, size, &offset);
695         if(type >= 1) {
696           trc->a = decodeICC15Fixed16(data, size, &offset);
697           trc->b = decodeICC15Fixed16(data, size, &offset);
698         }
699         if(type >= 2) {
700           trc->c = decodeICC15Fixed16(data, size, &offset);
701         }
702         if(type >= 3) {
703           trc->d = decodeICC15Fixed16(data, size, &offset);
704         }
705         if(type == 4) {
706           trc->e = decodeICC15Fixed16(data, size, &offset);
707           trc->f = decodeICC15Fixed16(data, size, &offset);
708         }
709       }
710       /* TODO: verify: does the "chrm" tag participate in computation so should be parsed? */
711     }
712     /* Return error if any parse went beyond the filesize. Note that the
713     parsing itself was always safe since it bound-checks inside. */
714     if(offset > size) return 1;
715   }
716 
717   return 0;
718 }
719 
720 /* Multiplies 3 vector values with 3x3 matrix */
mulMatrix(float * x2,float * y2,float * z2,const float * m,double x,double y,double z)721 static void mulMatrix(float* x2, float* y2, float* z2, const float* m, double x, double y, double z) {
722   /* double used as inputs even though in general the images are float, so the sums happen in
723   double precision, because float can give numerical problems for nearby values */
724   *x2 = x * m[0] + y * m[1] + z * m[2];
725   *y2 = x * m[3] + y * m[4] + z * m[5];
726   *z2 = x * m[6] + y * m[7] + z * m[8];
727 }
728 
mulMatrixMatrix(float * result,const float * a,const float * b)729 static void mulMatrixMatrix(float* result, const float* a, const float* b) {
730   int i;
731   float temp[9]; /* temp is to allow result and a or b to be the same */
732   mulMatrix(&temp[0], &temp[3], &temp[6], a, b[0], b[3], b[6]);
733   mulMatrix(&temp[1], &temp[4], &temp[7], a, b[1], b[4], b[7]);
734   mulMatrix(&temp[2], &temp[5], &temp[8], a, b[2], b[5], b[8]);
735   for(i = 0; i < 9; i++) result[i] = temp[i];
736 }
737 
738 /* Inverts 3x3 matrix in place */
invMatrix(float * m)739 static unsigned invMatrix(float* m) {
740   int i;
741   /* double used instead of float for intermediate computations to avoid
742   intermediate numerical precision issues */
743   double e0 = (double)m[4] * m[8] - (double)m[5] * m[7];
744   double e3 = (double)m[5] * m[6] - (double)m[3] * m[8];
745   double e6 = (double)m[3] * m[7] - (double)m[4] * m[6];
746   /* inverse determinant */
747   double d = 1.0 / (m[0] * e0 + m[1] * e3 + m[2] * e6);
748   float result[9];
749   if((d > 0 ? d : -d) > 1e15) return 1; /* error, likely not invertible */
750   result[0] = e0 * d;
751   result[1] = ((double)m[2] * m[7] - (double)m[1] * m[8]) * d;
752   result[2] = ((double)m[1] * m[5] - (double)m[2] * m[4]) * d;
753   result[3] = e3 * d;
754   result[4] = ((double)m[0] * m[8] - (double)m[2] * m[6]) * d;
755   result[5] = ((double)m[3] * m[2] - (double)m[0] * m[5]) * d;
756   result[6] = e6 * d;
757   result[7] = ((double)m[6] * m[1] - (double)m[0] * m[7]) * d;
758   result[8] = ((double)m[0] * m[4] - (double)m[3] * m[1]) * d;
759   for(i = 0; i < 9; i++) m[i] = result[i];
760   return 0; /* ok */
761 }
762 
763 /* Get the matrix to go from linear RGB to XYZ given the RGB whitepoint and chromaticities in XYZ colorspace */
getChrmMatrixXYZ(float * m,float wX,float wY,float wZ,float rX,float rY,float rZ,float gX,float gY,float gZ,float bX,float bY,float bZ)764 static unsigned getChrmMatrixXYZ(float* m,
765                                  float wX, float wY, float wZ,
766                                  float rX, float rY, float rZ,
767                                  float gX, float gY, float gZ,
768                                  float bX, float bY, float bZ) {
769   float t[9];
770   float rs, gs, bs;
771   t[0] = rX; t[1] = gX; t[2] = bX;
772   t[3] = rY; t[4] = gY; t[5] = bY;
773   t[6] = rZ; t[7] = gZ; t[8] = bZ;
774   if(invMatrix(t)) return 1; /* error, not invertible */
775   mulMatrix(&rs, &gs, &bs, t, wX, wY, wZ);
776   m[0] = rs * rX; m[1] = gs * gX; m[2] = bs * bX;
777   m[3] = rs * rY; m[4] = gs * gY; m[5] = bs * bY;
778   m[6] = rs * rZ; m[7] = gs * gZ; m[8] = bs * bZ;
779   return 0;
780 }
781 
782 /* Get the matrix to go from linear RGB to XYZ given the RGB whitepoint and chromaticities in xy colorspace */
getChrmMatrixXY(float * m,float wx,float wy,float rx,float ry,float gx,float gy,float bx,float by)783 static unsigned getChrmMatrixXY(float* m,
784                                 float wx, float wy,
785                                 float rx, float ry,
786                                 float gx, float gy,
787                                 float bx, float by) {
788   if(wy == 0 || ry == 0 || gy == 0 || by == 0) {
789     return 1; /* error, division through zero */
790   } else {
791     float wX = wx / wy, wY = 1, wZ = (1 - wx - wy) / wy;
792     float rX = rx / ry, rY = 1, rZ = (1 - rx - ry) / ry;
793     float gX = gx / gy, gY = 1, gZ = (1 - gx - gy) / gy;
794     float bX = bx / by, bY = 1, bZ = (1 - bx - by) / by;
795     return getChrmMatrixXYZ(m, wX, wY, wZ, rX, rY, rZ, gX, gY, gZ, bX, bY, bZ);
796   }
797 }
798 
799 /* Returns matrix that adapts from source whitepoint 0 to destination whitepoint 1.
800 Types: 0=XYZ scaling, 1=Bradford, 2=Vonkries */
getAdaptationMatrix(float * m,int type,float wx0,float wy0,float wz0,float wx1,float wy1,float wz1)801 static unsigned getAdaptationMatrix(float* m, int type,
802                                     float wx0, float wy0, float wz0,
803                                     float wx1, float wy1, float wz1) {
804   int i;
805   static const float bradford[9] = {
806     0.8951f, 0.2664f, -0.1614f,
807     -0.7502f, 1.7135f, 0.0367f,
808     0.0389f, -0.0685f, 1.0296f
809   };
810   static const float bradfordinv[9] = {
811     0.9869929f, -0.1470543f, 0.1599627f,
812     0.4323053f, 0.5183603f, 0.0492912f,
813    -0.0085287f, 0.0400428f, 0.9684867f
814   };
815   static const float vonkries[9] = {
816     0.40024f, 0.70760f, -0.08081f,
817     -0.22630f, 1.16532f, 0.04570f,
818     0.00000f, 0.00000f, 0.91822f,
819   };
820   static const float vonkriesinv[9] = {
821     1.8599364f, -1.1293816f, 0.2198974f,
822     0.3611914f, 0.6388125f, -0.0000064f,
823    0.0000000f, 0.0000000f, 1.0890636f
824   };
825   if(type == 0) {
826     for(i = 0; i < 9; i++) m[i] = 0;
827     m[0] = wx1 / wx0;
828     m[4] = wy1 / wy0;
829     m[8] = wz1 / wz0;
830   } else {
831     const float* cat = (type == 1) ? bradford : vonkries;
832     const float* inv = (type == 1) ? bradfordinv : vonkriesinv;
833     float rho0, gam0, bet0, rho1, gam1, bet1, rho2, gam2, bet2;
834     mulMatrix(&rho0, &gam0, &bet0, cat, wx0, wy0, wz0);
835     mulMatrix(&rho1, &gam1, &bet1, cat, wx1, wy1, wz1);
836     rho2 = rho1 / rho0;
837     gam2 = gam1 / gam0;
838     bet2 = bet1 / bet0;
839     /* Multiply diagonal matrix with cat */
840     for(i = 0; i < 3; i++) {
841       m[i + 0] = rho2 * cat[i + 0];
842       m[i + 3] = gam2 * cat[i + 3];
843       m[i + 6] = bet2 * cat[i + 6];
844     }
845     mulMatrixMatrix(m, inv, m);
846   }
847   return 0; /* ok */
848 }
849 
850 /* validate whether the ICC profile is supported here for PNG */
validateICC(const LodePNGICC * icc)851 static unsigned validateICC(const LodePNGICC* icc) {
852   /* disable for unsupported things in the icc profile */
853   if(icc->inputspace == 0) return 0;
854   /* if we didn't recognize both chrm and trc, then maybe the ICC uses data
855   types not supported here yet, so fall back to not using it. */
856   if(icc->inputspace == 2) {
857     /* RGB profile should have chromaticities */
858     if(!icc->has_chromaticity) return 0;
859   }
860   /* An ICC profile without whitepoint is invalid for the kind of profiles used here. */
861   if(!icc->has_whitepoint) return 0;
862   if(!icc->has_trc) return 0;
863   return 1; /* ok */
864 }
865 
866 /* Returns chromaticity matrix for given ICC profile, adapted from ICC's
867 global illuminant as necessary.
868 Also returns the profile's whitepoint.
869 In case of a gray profile (icc->inputspace == 1), the identity matrix will be returned
870 so in that case you could skip the transform. */
getICCChrm(float m[9],float whitepoint[3],const LodePNGICC * icc)871 static unsigned getICCChrm(float m[9], float whitepoint[3], const LodePNGICC* icc) {
872   size_t i;
873   if(icc->inputspace == 2) { /* RGB profile */
874     float red[3], green[3], blue[3];
875     float white[3]; /* the whitepoint of the RGB color space (absolute) */
876     /* Adaptation matrix a.
877     This is an adaptation needed for ICC's file format (due to it using
878     an internal global illuminant unrelated to the actual images) */
879     float a[9] = {1,0,0, 0,1,0, 0,0,1};
880     /* If the profile has chromatic adaptation matrix "chad", use that one,
881     else compute it from the illuminant and whitepoint. */
882     if(icc->has_chad) {
883       for(i = 0; i < 9; i++) a[i] = icc->chad[i];
884       invMatrix(a);
885     } else {
886       if(getAdaptationMatrix(a, 1, icc->illuminant[0], icc->illuminant[1], icc->illuminant[2],
887                              icc->white[0], icc->white[1], icc->white[2])) {
888         return 1; /* error computing matrix */
889       }
890     }
891     /* If the profile has a chad, then also the RGB's whitepoint must also be adapted from it (and the one
892     given is normally D50). If it did not have a chad, then the whitepoint given is already the adapted one. */
893     if(icc->has_chad) {
894       mulMatrix(&white[0], &white[1], &white[2], a, icc->white[0], icc->white[1], icc->white[2]);
895     } else {
896       for(i = 0; i < 3; i++) white[i] = icc->white[i];
897     }
898 
899     mulMatrix(&red[0], &red[1], &red[2], a, icc->red[0], icc->red[1], icc->red[2]);
900     mulMatrix(&green[0], &green[1], &green[2], a, icc->green[0], icc->green[1], icc->green[2]);
901     mulMatrix(&blue[0], &blue[1], &blue[2], a, icc->blue[0], icc->blue[1], icc->blue[2]);
902 
903     if(getChrmMatrixXYZ(m, white[0], white[1], white[2], red[0], red[1], red[2],
904                         green[0], green[1], green[2], blue[0], blue[1], blue[2])) {
905       return 1; /* error computing matrix */
906     }
907     /* output absolute whitepoint of the original RGB model */
908     whitepoint[0] = white[0];
909     whitepoint[1] = white[1];
910     whitepoint[2] = white[2];
911   } else {
912     /* output the unity matrix, for doing no transform */
913     m[0] = m[4] = m[8] = 1;
914     m[1] = m[2] = m[3] = m[5] = m[6] = m[7] = 0;
915     /* grayscale, don't do anything. That means we are implicitely using equal energy whitepoint "E", indicate
916     this to the output. */
917     whitepoint[0] = whitepoint[1] = whitepoint[2] = 1;
918   }
919   return 0; /* success */
920 }
921 
922 /* Outputs whitepoint and matrix to go from the icc or info profile (depending on what was in the PNG) to XYZ,
923 without applying any (rendering intent related) whitepoint adaptation */
getChrm(float m[9],float whitepoint[3],unsigned use_icc,const LodePNGICC * icc,const LodePNGInfo * info)924 static unsigned getChrm(float m[9], float whitepoint[3], unsigned use_icc,
925                         const LodePNGICC* icc, const LodePNGInfo* info) {
926   size_t i;
927   if(use_icc) {
928     if(getICCChrm(m, whitepoint, icc)) return 1;  /* error in the matrix computations */
929   } else if(info->chrm_defined && !info->srgb_defined) {
930     float wx = info->chrm_white_x / 100000.0f, wy = info->chrm_white_y / 100000.0f;
931     float rx = info->chrm_red_x / 100000.0f, ry = info->chrm_red_y / 100000.0f;
932     float gx = info->chrm_green_x / 100000.0f, gy = info->chrm_green_y / 100000.0f;
933     float bx = info->chrm_blue_x / 100000.0f, by = info->chrm_blue_y / 100000.0f;
934     if(getChrmMatrixXY(m, wx, wy, rx, ry, gx, gy, bx, by)) return 1; /* returns if error */
935     /* Output whitepoint, xyY to XYZ: */
936     whitepoint[0] = wx / wy;
937     whitepoint[1] = 1;
938     whitepoint[2] = (1 - wx - wy) / wy;
939   } else {
940     /* the standard linear sRGB to XYZ matrix */
941     static const float srgb[9] = {
942         0.4124564f, 0.3575761f, 0.1804375f,
943         0.2126729f, 0.7151522f, 0.0721750f,
944         0.0193339f, 0.1191920f, 0.9503041f
945     };
946     for(i = 0; i < 9; i++) m[i] = srgb[i];
947     /* sRGB's whitepoint xyY "0.3127,0.3290,1" in XYZ: */
948     whitepoint[0] = 0.9504559270516716f;
949     whitepoint[1] = 1;
950     whitepoint[2] = 1.0890577507598784f;
951   }
952   return 0;
953 }
954 
955 /* Returns whether the color chunks in info represent the default PNG sRGB,
956 which is when either no colorometry fields are present at all, or an srgb
957 field or chrm/gama field with default values are present.
958 ICC chunks representing sRGB are currently considered not the same. */
isSRGB(const LodePNGInfo * info)959 static unsigned isSRGB(const LodePNGInfo* info) {
960   if(!info) return 1; /* the default is considered sRGB. */
961 
962   /* TODO: support some ICC profiles that represent sRGB too. Tricky due to
963   possible slight deviations and many ways of representing its gamma function. */
964   if(info->iccp_defined) return 0;
965 
966   if(info->srgb_defined) return 1;
967 
968   /* The gamma chunk is unable to represent sRGB's two-part gamma, so cannot
969   be sRGB, even if it's the default 45455. */
970   if(info->gama_defined) return 0;
971 
972   if(info->chrm_defined) {
973     if(info->chrm_white_x != 31270 || info->chrm_white_y != 32900) return 0;
974     if(info->chrm_red_x != 64000 || info->chrm_red_y != 33000) return 0;
975     if(info->chrm_green_x != 30000 || info->chrm_green_y != 60000) return 0;
976     if(info->chrm_blue_x != 15000 || info->chrm_blue_y != 6000) return 0;
977   }
978 
979   return 1;
980 }
981 
982 /* Checks whether the RGB models are equal (chromaticities, ...). The raw byte
983 format is allowed to be different. Input pointers are allowed to be null,
984 they then represent the default PNG sRGB (same as having no color model
985 chunks at all or an srgb chunk in the PNG) */
modelsEqual(const LodePNGState * state_a,const LodePNGState * state_b)986 static unsigned modelsEqual(const LodePNGState* state_a,
987                             const LodePNGState* state_b) {
988   size_t i;
989   const LodePNGInfo* a = state_a ? &state_a->info_png : 0;
990   const LodePNGInfo* b = state_b ? &state_b->info_png : 0;
991   if(isSRGB(a) != isSRGB(b)) return 0;
992   /* now a and b are guaranteed to be non-NULL */
993   if(a->iccp_defined != b->iccp_defined) return 0;
994   if(a->iccp_defined) {
995     if(a->iccp_profile_size != b->iccp_profile_size) return 0;
996     /* TODO: return equal in more cases, such as when two ICC profiles that are
997     not byte-for-byte equal, but represent the same color model. */
998     for(i = 0; i < a->iccp_profile_size; i++) {
999       if(a->iccp_profile[i] != b->iccp_profile[i]) return 0;
1000     }
1001     /* since the ICC model overrides gamma and chrm, those can be ignored. */
1002     /* TODO: this doesn't cover the case where the ICC profile is invalid */
1003     return 1;
1004   }
1005 
1006   if(a->srgb_defined != b->srgb_defined) return 0;
1007   if(a->srgb_defined) {
1008     /* since the sRGB model overrides gamma and chrm, those can be ignored.
1009     srgb_intent not checked since the conversion ignores it */
1010     return 1;
1011   }
1012 
1013   if(a->gama_defined != b->gama_defined) return 0;
1014   if(a->gama_defined) {
1015     if(a->gama_gamma != b->gama_gamma) return 0;
1016   }
1017 
1018   if(a->chrm_defined != b->chrm_defined) return 0;
1019   if(a->chrm_defined) {
1020     if(a->chrm_white_x != b->chrm_white_x) return 0;
1021     if(a->chrm_white_y != b->chrm_white_y) return 0;
1022     if(a->chrm_red_x != b->chrm_red_x) return 0;
1023     if(a->chrm_red_y != b->chrm_red_y) return 0;
1024     if(a->chrm_green_x != b->chrm_green_x) return 0;
1025     if(a->chrm_green_y != b->chrm_green_y) return 0;
1026     if(a->chrm_blue_x != b->chrm_blue_x) return 0;
1027     if(a->chrm_blue_y != b->chrm_blue_y) return 0;
1028   }
1029 
1030   return 1;
1031 }
1032 
1033 /* Converts in-place. Does not clamp. Do not use for integer input, make table instead there. */
convertToXYZ_gamma(float * out,const float * in,unsigned w,unsigned h,const LodePNGInfo * info,unsigned use_icc,const LodePNGICC * icc)1034 static void convertToXYZ_gamma(float* out, const float* in, unsigned w, unsigned h,
1035                                const LodePNGInfo* info, unsigned use_icc, const LodePNGICC* icc) {
1036   size_t i, c;
1037   size_t n = w * h;
1038   for(i = 0; i < n * 4; i++) {
1039     out[i] = in[i];
1040   }
1041   if(use_icc) {
1042     for(i = 0; i < n; i++) {
1043       for(c = 0; c < 3; c++) {
1044         /* TODO: this is likely very slow */
1045         out[i * 4 + c] = iccForwardTRC(&icc->trc[c], in[i * 4 + c]);
1046       }
1047     }
1048   } else if(info->gama_defined && !info->srgb_defined) {
1049     /* nothing to do if gamma is 1 */
1050     if(info->gama_gamma != 100000) {
1051       float gamma = 100000.0f / info->gama_gamma;
1052       for(i = 0; i < n; i++) {
1053         for(c = 0; c < 3; c++) {
1054           float v = in[i * 4 + c];
1055           out[i * 4 + c] = (v <= 0) ? v : lodepng_powf(v, gamma);
1056         }
1057       }
1058     }
1059   } else {
1060     for(i = 0; i < n; i++) {
1061       for(c = 0; c < 3; c++) {
1062         /* sRGB gamma expand */
1063         float v = in[i * 4 + c];
1064         out[i * 4 + c] = (v < 0.04045f) ? (v / 12.92f) : lodepng_powf((v + 0.055f) / 1.055f, 2.4f);
1065       }
1066     }
1067   }
1068 }
1069 
1070 /* Same as convertToXYZ_gamma, but creates a lookup table rather than operating on an image */
convertToXYZ_gamma_table(float * out,size_t n,size_t c,const LodePNGInfo * info,unsigned use_icc,const LodePNGICC * icc)1071 static void convertToXYZ_gamma_table(float* out, size_t n, size_t c,
1072                                      const LodePNGInfo* info, unsigned use_icc, const LodePNGICC* icc) {
1073   size_t i;
1074   float mul = 1.0f / (n - 1);
1075   if(use_icc) {
1076     for(i = 0; i < n; i++) {
1077       float v = i * mul;
1078       out[i] = iccForwardTRC(&icc->trc[c], v);
1079     }
1080   } else if(info->gama_defined && !info->srgb_defined) {
1081     /* no power needed if gamma is 1 */
1082     if(info->gama_gamma == 100000) {
1083       for(i = 0; i < n; i++) {
1084         out[i] = i * mul;
1085       }
1086     } else {
1087       float gamma = 100000.0f / info->gama_gamma;
1088       for(i = 0; i < n; i++) {
1089         float v = i * mul;
1090         out[i] = lodepng_powf(v, gamma);
1091       }
1092     }
1093   } else {
1094     for(i = 0; i < n; i++) {
1095       /* sRGB gamma expand */
1096       float v = i * mul;
1097       out[i] = (v < 0.04045f) ? (v / 12.92f) : lodepng_powf((v + 0.055f) / 1.055f, 2.4f);
1098     }
1099   }
1100 }
1101 
1102 /* In-place */
convertToXYZ_chrm(float * im,unsigned w,unsigned h,const LodePNGInfo * info,unsigned use_icc,const LodePNGICC * icc,float whitepoint[3])1103 static unsigned convertToXYZ_chrm(float* im, unsigned w, unsigned h,
1104                                   const LodePNGInfo* info, unsigned use_icc, const LodePNGICC* icc,
1105                                   float whitepoint[3]) {
1106   unsigned error = 0;
1107   size_t i;
1108   size_t n = w * h;
1109   float m[9]; /* XYZ to linear RGB matrix */
1110 
1111   /* Must be called even for grayscale, to get the correct whitepoint to output */
1112   error = getChrm(m, whitepoint, use_icc, icc, info);
1113   if(error) return error;
1114 
1115   /* Note: no whitepoint adaptation done to m here, because we only do the
1116   adaptation in convertFromXYZ (we only whitepoint adapt when going to the
1117   target RGB space, but here we're going from the source RGB space to XYZ) */
1118 
1119   /* Apply the above computed linear-RGB-to-XYZ matrix to the pixels.
1120   Skip the transform if it's the unit matrix (which is the case if grayscale profile) */
1121   if(!use_icc || icc->inputspace == 2) {
1122     for(i = 0; i < n; i++) {
1123       size_t j = i * 4;
1124       mulMatrix(&im[j + 0], &im[j + 1], &im[j + 2], m, im[j + 0], im[j + 1], im[j + 2]);
1125     }
1126   }
1127 
1128   return 0;
1129 }
1130 
convertToXYZ(float * out,float whitepoint[3],const unsigned char * in,unsigned w,unsigned h,const LodePNGState * state)1131 unsigned convertToXYZ(float* out, float whitepoint[3], const unsigned char* in,
1132                       unsigned w, unsigned h, const LodePNGState* state) {
1133   unsigned error = 0;
1134   size_t i;
1135   size_t n = w * h;
1136   const LodePNGColorMode* mode_in = &state->info_raw;
1137   const LodePNGInfo* info = &state->info_png;
1138   unsigned char* data = 0;
1139   float* gammatable = 0;
1140   int bit16 = mode_in->bitdepth > 8;
1141   size_t num = bit16 ? 65536 : 256;
1142   LodePNGColorMode tempmode = lodepng_color_mode_make(LCT_RGBA, bit16 ? 16 : 8);
1143 
1144 
1145   unsigned use_icc = 0;
1146   LodePNGICC icc;
1147   lodepng_icc_init(&icc);
1148   if(info->iccp_defined) {
1149     error = parseICC(&icc, info->iccp_profile, info->iccp_profile_size);
1150     if(error) goto cleanup; /* corrupted ICC profile */
1151     use_icc = validateICC(&icc);
1152   }
1153 
1154   data = (unsigned char*)lodepng_malloc(w * h * (bit16 ? 8 : 4));
1155   error = lodepng_convert(data, in, &tempmode, mode_in, w, h);
1156   if(error) goto cleanup;
1157 
1158   /* Handle transfer function */
1159   {
1160     float* gammatable_r;
1161     float* gammatable_g;
1162     float* gammatable_b;
1163 
1164     /* RGB ICC, can have three different transfer functions */
1165     if(use_icc && icc.inputspace == 2) {
1166       gammatable = (float*)lodepng_malloc(num * 3 * sizeof(float));
1167       gammatable_r = &gammatable[num * 0];
1168       gammatable_g = &gammatable[num * 1];
1169       gammatable_b = &gammatable[num * 2];
1170       convertToXYZ_gamma_table(gammatable_r, num, 0, info, use_icc, &icc);
1171       convertToXYZ_gamma_table(gammatable_g, num, 1, info, use_icc, &icc);
1172       convertToXYZ_gamma_table(gammatable_b, num, 2, info, use_icc, &icc);
1173     } else {
1174       gammatable = (float*)lodepng_malloc(num * sizeof(float));
1175       gammatable_r = gammatable_g = gammatable_b = gammatable;
1176       convertToXYZ_gamma_table(gammatable, num, 0, info, use_icc, &icc);
1177     }
1178 
1179     if(bit16) {
1180       for(i = 0; i < n; i++) {
1181         out[i * 4 + 0] = gammatable_r[data[i * 8 + 0] * 256u + data[i * 8 + 1]];
1182         out[i * 4 + 1] = gammatable_g[data[i * 8 + 2] * 256u + data[i * 8 + 3]];
1183         out[i * 4 + 2] = gammatable_b[data[i * 8 + 4] * 256u + data[i * 8 + 5]];
1184         out[i * 4 + 3] = (data[i * 8 + 6] * 256 + data[i * 8 + 7]) * (1 / 65535.0f);
1185       }
1186     } else {
1187       for(i = 0; i < n; i++) {
1188         out[i * 4 + 0] = gammatable_r[data[i * 4 + 0]];
1189         out[i * 4 + 1] = gammatable_g[data[i * 4 + 1]];
1190         out[i * 4 + 2] = gammatable_b[data[i * 4 + 2]];
1191         out[i * 4 + 3] = data[i * 4 + 3] * (1 / 255.0f);
1192       }
1193     }
1194   }
1195 
1196   convertToXYZ_chrm(out, w, h, info, use_icc, &icc, whitepoint);
1197 
1198 cleanup:
1199   lodepng_icc_cleanup(&icc);
1200   lodepng_free(data);
1201   lodepng_free(gammatable);
1202   return error;
1203 }
1204 
convertToXYZFloat(float * out,float whitepoint[3],const float * in,unsigned w,unsigned h,const LodePNGState * state)1205 unsigned convertToXYZFloat(float* out, float whitepoint[3], const float* in,
1206                            unsigned w, unsigned h, const LodePNGState* state) {
1207   unsigned error = 0;
1208   const LodePNGInfo* info = &state->info_png;
1209 
1210   unsigned use_icc = 0;
1211   LodePNGICC icc;
1212   lodepng_icc_init(&icc);
1213   if(info->iccp_defined) {
1214     error = parseICC(&icc, info->iccp_profile, info->iccp_profile_size);
1215     if(error) goto cleanup; /* corrupted ICC profile */
1216     use_icc = validateICC(&icc);
1217   }
1218   /* Input is floating point, so lookup table cannot be used, but it's ensured to
1219   use float pow, not the slower double pow. */
1220   convertToXYZ_gamma(out, in, w, h, info, use_icc, &icc);
1221   convertToXYZ_chrm(out, w, h, info, use_icc, &icc, whitepoint);
1222 
1223 cleanup:
1224   lodepng_icc_cleanup(&icc);
1225   return error;
1226 }
1227 
convertFromXYZ_chrm(float * out,const float * in,unsigned w,unsigned h,const LodePNGInfo * info,unsigned use_icc,const LodePNGICC * icc,const float whitepoint[3],unsigned rendering_intent)1228 static unsigned convertFromXYZ_chrm(float* out, const float* in, unsigned w, unsigned h,
1229                                     const LodePNGInfo* info, unsigned use_icc, const LodePNGICC* icc,
1230                                     const float whitepoint[3], unsigned rendering_intent) {
1231   size_t i;
1232   size_t n = w * h;
1233 
1234   float m[9]; /* XYZ to linear RGB matrix */
1235   float white[3]; /* The whitepoint (absolute) of the target RGB space */
1236 
1237   if(getChrm(m, white, use_icc, icc, info)) return 1;
1238   if(invMatrix(m)) return 1; /* error, not invertible */
1239 
1240   /* for relative rendering intent (any except absolute "3"), must whitepoint adapt to the original whitepoint.
1241   this also ensures grayscale stays grayscale (with absolute, grayscale could become e.g. blue or sepia) */
1242   if(rendering_intent != 3) {
1243     float a[9] = {1,0,0, 0,1,0, 0,0,1};
1244     /* "white" = absolute whitepoint of the new target RGB space, read from the target color profile.
1245     "whitepoint" is original absolute whitepoint (input as parameter of this function) of an
1246     RGB space the XYZ data once had before it was converted to XYZ, in other words the whitepoint that
1247     we want to adapt our current data to to make sure values that had equal R==G==B in the old space have
1248     the same property now (white stays white and gray stays gray).
1249     Note: "absolute" whitepoint above means, can be used as-is, not needing further adaptation itself like icc.white does.*/
1250     if(getAdaptationMatrix(a, 1, whitepoint[0], whitepoint[1], whitepoint[2], white[0], white[1], white[2])) {
1251       return 1;
1252     }
1253     /* multiply the from xyz matrix with the adaptation matrix: in total,
1254     the resulting matrix first adapts in XYZ space, then converts to RGB*/
1255     mulMatrixMatrix(m, m, a);
1256   }
1257 
1258   /* Apply the above computed XYZ-to-linear-RGB matrix to the pixels.
1259   This transformation also includes the whitepoint adaptation. The transform
1260   can be skipped only if it's the unit matrix (only if grayscale profile and no
1261   whitepoint adaptation, such as with rendering intent 3)*/
1262   if(!use_icc || icc->inputspace == 2 || rendering_intent != 3) {
1263     for(i = 0; i < n; i++) {
1264       size_t j = i * 4;
1265       mulMatrix(&out[j + 0], &out[j + 1], &out[j + 2], m, in[j + 0], in[j + 1], in[j + 2]);
1266       out[j + 3] = in[j + 3];
1267     }
1268   } else {
1269     for(i = 0; i < n * 4; i++) {
1270       out[i] = in[i];
1271     }
1272   }
1273 
1274   return 0;
1275 }
1276 
1277 /* Converts in-place. Does not clamp. */
convertFromXYZ_gamma(float * im,unsigned w,unsigned h,const LodePNGInfo * info,unsigned use_icc,const LodePNGICC * icc)1278 static void convertFromXYZ_gamma(float* im, unsigned w, unsigned h,
1279                                  const LodePNGInfo* info, unsigned use_icc, const LodePNGICC* icc) {
1280   size_t i, c;
1281   size_t n = w * h;
1282   if(use_icc) {
1283     for(i = 0; i < n; i++) {
1284       for(c = 0; c < 3; c++) {
1285         /* TODO: this is likely very slow */
1286         im[i * 4 + c] = iccBackwardTRC(&icc->trc[c], im[i * 4 + c]);
1287       }
1288     }
1289   } else if(info->gama_defined && !info->srgb_defined) {
1290     /* nothing to do if gamma is 1 */
1291     if(info->gama_gamma != 100000) {
1292       float gamma = info->gama_gamma / 100000.0f;
1293       for(i = 0; i < n; i++) {
1294         for(c = 0; c < 3; c++) {
1295           if(im[i * 4 + c] > 0) im[i * 4 + c] = lodepng_powf(im[i * 4 + c], gamma);
1296         }
1297       }
1298     }
1299   } else {
1300     for(i = 0; i < n; i++) {
1301       for(c = 0; c < 3; c++) {
1302         /* sRGB gamma compress */
1303         float* v = &im[i * 4 + c];
1304         *v = (*v < 0.0031308f) ? (*v * 12.92f) : (1.055f * lodepng_powf(*v, 1 / 2.4f) - 0.055f);
1305       }
1306     }
1307   }
1308 }
1309 
convertFromXYZ(unsigned char * out,const float * in,unsigned w,unsigned h,const LodePNGState * state,const float whitepoint[3],unsigned rendering_intent)1310 unsigned convertFromXYZ(unsigned char* out, const float* in, unsigned w, unsigned h,
1311                         const LodePNGState* state,
1312                         const float whitepoint[3], unsigned rendering_intent) {
1313   unsigned error = 0;
1314   size_t i, c;
1315   size_t n = w * h;
1316   const LodePNGColorMode* mode_out = &state->info_raw;
1317   const LodePNGInfo* info = &state->info_png;
1318   int bit16 = mode_out->bitdepth > 8;
1319   float* im = 0;
1320   unsigned char* data = 0;
1321 
1322   /* parse ICC if present */
1323   unsigned use_icc = 0;
1324   LodePNGICC icc;
1325   lodepng_icc_init(&icc);
1326   if(info->iccp_defined) {
1327     error = parseICC(&icc, info->iccp_profile, info->iccp_profile_size);
1328     if(error) goto cleanup; /* corrupted ICC profile */
1329     use_icc = validateICC(&icc);
1330   }
1331 
1332   /* Handle gamut */
1333   im = (float*)lodepng_malloc(w * h * 4 * sizeof(float));
1334   error = convertFromXYZ_chrm(im, in, w, h, info, use_icc, &icc, whitepoint, rendering_intent);
1335   if(error) goto cleanup;
1336 
1337   /* Handle transfer function */
1338   /* Input is floating point, so lookup table cannot be used, but it's ensured to use float pow, not the slower double pow. */
1339   convertFromXYZ_gamma(im, w, h, info, use_icc, &icc);
1340 
1341   /* Convert to integer output */
1342   data = (unsigned char*)lodepng_malloc(w * h * 8);
1343   /* TODO: check if also 1/2/4 bit case needed: rounding is at different fine-grainedness for 8 and 16 bits below. */
1344   if(bit16) {
1345     LodePNGColorMode mode16 = lodepng_color_mode_make(LCT_RGBA, 16);
1346     for(i = 0; i < n; i++) {
1347       for(c = 0; c < 4; c++) {
1348         size_t j = i * 8 + c * 2;
1349         int i16 = (int)(0.5f + 65535.0f * LODEPNG_MIN(LODEPNG_MAX(0.0f, im[i * 4 + c]), 1.0f));
1350         data[j + 0] = i16 >> 8;
1351         data[j + 1] = i16 & 255;
1352       }
1353     }
1354     error = lodepng_convert(out, data, mode_out, &mode16, w, h);
1355     if(error) goto cleanup;
1356   } else {
1357     LodePNGColorMode mode8 = lodepng_color_mode_make(LCT_RGBA, 8);
1358     for(i = 0; i < n; i++) {
1359       for(c = 0; c < 4; c++) {
1360         int i8 = (int)(0.5f + 255.0f * LODEPNG_MIN(LODEPNG_MAX(0.0f, im[i * 4 + c]), 1.0f));
1361         data[i * 4 + c] = i8;
1362       }
1363     }
1364     error = lodepng_convert(out, data, mode_out, &mode8, w, h);
1365     if(error) goto cleanup;
1366   }
1367 
1368 cleanup:
1369   lodepng_icc_cleanup(&icc);
1370   lodepng_free(im);
1371   lodepng_free(data);
1372   return error;
1373 }
1374 
convertFromXYZFloat(float * out,const float * in,unsigned w,unsigned h,const LodePNGState * state,const float whitepoint[3],unsigned rendering_intent)1375 unsigned convertFromXYZFloat(float* out, const float* in, unsigned w, unsigned h,
1376                              const LodePNGState* state,
1377                              const float whitepoint[3], unsigned rendering_intent) {
1378   unsigned error = 0;
1379   const LodePNGInfo* info = &state->info_png;
1380 
1381   /* parse ICC if present */
1382   unsigned use_icc = 0;
1383   LodePNGICC icc;
1384   lodepng_icc_init(&icc);
1385   if(info->iccp_defined) {
1386     error = parseICC(&icc, info->iccp_profile, info->iccp_profile_size);
1387     if(error) goto cleanup; /* corrupted ICC profile */
1388     use_icc = validateICC(&icc);
1389   }
1390 
1391   /* Handle gamut */
1392   error = convertFromXYZ_chrm(out, in, w, h, info, use_icc, &icc, whitepoint, rendering_intent);
1393   if(error) goto cleanup;
1394 
1395   /* Handle transfer function */
1396   convertFromXYZ_gamma(out, w, h, info, use_icc, &icc);
1397 
1398 cleanup:
1399   lodepng_icc_cleanup(&icc);
1400   return error;
1401 }
1402 
convertRGBModel(unsigned char * out,const unsigned char * in,unsigned w,unsigned h,const LodePNGState * state_out,const LodePNGState * state_in,unsigned rendering_intent)1403 unsigned convertRGBModel(unsigned char* out, const unsigned char* in,
1404                          unsigned w, unsigned h,
1405                          const LodePNGState* state_out,
1406                          const LodePNGState* state_in,
1407                          unsigned rendering_intent) {
1408   if(modelsEqual(state_in, state_out)) {
1409     return lodepng_convert(out, in, &state_out->info_raw, &state_in->info_raw, w, h);
1410   } else {
1411     unsigned error = 0;
1412     float* xyz = (float*)lodepng_malloc(w * h * 4 * sizeof(float));
1413     float whitepoint[3];
1414     error = convertToXYZ(&xyz[0], whitepoint, in, w, h, state_in);
1415     if (!error) error = convertFromXYZ(out, &xyz[0], w, h, state_out, whitepoint, rendering_intent);
1416     lodepng_free(xyz);
1417     return error;
1418   }
1419 }
1420 
convertToSrgb(unsigned char * out,const unsigned char * in,unsigned w,unsigned h,const LodePNGState * state_in)1421 unsigned convertToSrgb(unsigned char* out, const unsigned char* in,
1422                        unsigned w, unsigned h,
1423                        const LodePNGState* state_in) {
1424   LodePNGState srgb;
1425   lodepng_state_init(&srgb);
1426   lodepng_color_mode_copy(&srgb.info_raw, &state_in->info_raw);
1427   return convertRGBModel(out, in, w, h, &srgb, state_in, 1);
1428 }
1429 
convertFromSrgb(unsigned char * out,const unsigned char * in,unsigned w,unsigned h,const LodePNGState * state_out)1430 unsigned convertFromSrgb(unsigned char* out, const unsigned char* in,
1431                          unsigned w, unsigned h,
1432                          const LodePNGState* state_out) {
1433   LodePNGState srgb;
1434   lodepng_state_init(&srgb);
1435   lodepng_color_mode_copy(&srgb.info_raw, &state_out->info_raw);
1436   return convertRGBModel(out, in, w, h, state_out, &srgb, 1);
1437 }
1438 
1439 #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
1440 
1441 ////////////////////////////////////////////////////////////////////////////////
1442 
1443 
1444 
1445 //This uses a stripped down version of picoPNG to extract detailed zlib information while decompressing.
1446 static const unsigned long LENBASE[29] = {3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258};
1447 static const unsigned long LENEXTRA[29] = {0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,  4,  5,  5,  5,  5,  0};
1448 static const unsigned long DISTBASE[30] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577};
1449 static const unsigned long DISTEXTRA[30] = {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5,  6,  6,  7,  7,  8,  8,   9,   9,  10,  10,  11,  11,  12,   12,   13,   13};
1450 static const unsigned long CLCL[19] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; //code length code lengths
1451 
1452 struct ExtractZlib { // Zlib decompression and information extraction
1453   std::vector<ZlibBlockInfo>* zlibinfo;
ExtractZliblodepng::ExtractZlib1454   ExtractZlib(std::vector<ZlibBlockInfo>* info) : zlibinfo(info) {};
1455   int error;
1456 
readBitFromStreamlodepng::ExtractZlib1457   unsigned long readBitFromStream(size_t& bitp, const unsigned char* bits) {
1458     unsigned long result = (bits[bitp >> 3] >> (bitp & 0x7)) & 1;
1459     bitp++;
1460     return result;
1461   }
1462 
readBitsFromStreamlodepng::ExtractZlib1463   unsigned long readBitsFromStream(size_t& bitp, const unsigned char* bits, size_t nbits) {
1464     unsigned long result = 0;
1465     for(size_t i = 0; i < nbits; i++) result += (readBitFromStream(bitp, bits)) << i;
1466     return result;
1467   }
1468 
1469   struct HuffmanTree {
makeFromLengthslodepng::ExtractZlib::HuffmanTree1470     int makeFromLengths(const std::vector<unsigned long>& bitlen, unsigned long maxbitlen) { //make tree given the lengths
1471       unsigned long numcodes = (unsigned long)(bitlen.size()), treepos = 0, nodefilled = 0;
1472       std::vector<unsigned long> tree1d(numcodes), blcount(maxbitlen + 1, 0), nextcode(maxbitlen + 1, 0);
1473       //count number of instances of each code length
1474       for(unsigned long bits = 0; bits < numcodes; bits++) blcount[bitlen[bits]]++;
1475       for(unsigned long bits = 1; bits <= maxbitlen; bits++) {
1476         nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1;
1477       }
1478       //generate all the codes
1479       for(unsigned long n = 0; n < numcodes; n++) if(bitlen[n] != 0) tree1d[n] = nextcode[bitlen[n]]++;
1480       tree2d.clear(); tree2d.resize(numcodes * 2, 32767); //32767 here means the tree2d isn't filled there yet
1481       for(unsigned long n = 0; n < numcodes; n++) //the codes
1482       for(unsigned long i = 0; i < bitlen[n]; i++) { //the bits for this code
1483         unsigned long bit = (tree1d[n] >> (bitlen[n] - i - 1)) & 1;
1484         if(treepos > numcodes - 2) return 55;
1485         if(tree2d[2 * treepos + bit] == 32767) { //not yet filled in
1486           if(i + 1 == bitlen[n]) {
1487             //last bit
1488             tree2d[2 * treepos + bit] = n;
1489             treepos = 0;
1490           } else {
1491             //addresses are encoded as values > numcodes
1492             tree2d[2 * treepos + bit] = ++nodefilled + numcodes;
1493             treepos = nodefilled;
1494           }
1495         }
1496         else treepos = tree2d[2 * treepos + bit] - numcodes; //subtract numcodes from address to get address value
1497       }
1498       return 0;
1499     }
decodelodepng::ExtractZlib::HuffmanTree1500     int decode(bool& decoded, unsigned long& result, size_t& treepos, unsigned long bit) const { //Decodes a symbol from the tree
1501       unsigned long numcodes = (unsigned long)tree2d.size() / 2;
1502       if(treepos >= numcodes) return 11; //error: you appeared outside the codetree
1503       result = tree2d[2 * treepos + bit];
1504       decoded = (result < numcodes);
1505       treepos = decoded ? 0 : result - numcodes;
1506       return 0;
1507     }
1508     //2D representation of a huffman tree: one dimension is "0" or "1", the other contains all nodes and leaves.
1509     std::vector<unsigned long> tree2d;
1510   };
1511 
inflatelodepng::ExtractZlib1512   void inflate(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, size_t inpos = 0) {
1513     size_t bp = 0, pos = 0; //bit pointer and byte pointer
1514     error = 0;
1515     unsigned long BFINAL = 0;
1516     while(!BFINAL && !error) {
1517       size_t uncomprblockstart = pos;
1518       size_t bpstart = bp;
1519       if(bp >> 3 >= in.size()) { error = 52; return; } //error, bit pointer will jump past memory
1520       BFINAL = readBitFromStream(bp, &in[inpos]);
1521       unsigned long BTYPE = readBitFromStream(bp, &in[inpos]); BTYPE += 2 * readBitFromStream(bp, &in[inpos]);
1522       zlibinfo->resize(zlibinfo->size() + 1);
1523       zlibinfo->back().btype = BTYPE;
1524       if(BTYPE == 3) { error = 20; return; } //error: invalid BTYPE
1525       else if(BTYPE == 0) inflateNoCompression(out, &in[inpos], bp, pos, in.size());
1526       else inflateHuffmanBlock(out, &in[inpos], bp, pos, in.size(), BTYPE);
1527       size_t uncomprblocksize = pos - uncomprblockstart;
1528       zlibinfo->back().compressedbits = bp - bpstart;
1529       zlibinfo->back().uncompressedbytes = uncomprblocksize;
1530     }
1531   }
1532 
generateFixedTreeslodepng::ExtractZlib1533   void generateFixedTrees(HuffmanTree& tree, HuffmanTree& treeD) { //get the tree of a deflated block with fixed tree
1534     std::vector<unsigned long> bitlen(288, 8), bitlenD(32, 5);;
1535     for(size_t i = 144; i <= 255; i++) bitlen[i] = 9;
1536     for(size_t i = 256; i <= 279; i++) bitlen[i] = 7;
1537     tree.makeFromLengths(bitlen, 15);
1538     treeD.makeFromLengths(bitlenD, 15);
1539   }
1540 
1541   //the code tree for Huffman codes, dist codes, and code length codes
1542   HuffmanTree codetree, codetreeD, codelengthcodetree;
huffmanDecodeSymbollodepng::ExtractZlib1543   unsigned long huffmanDecodeSymbol(const unsigned char* in, size_t& bp, const HuffmanTree& tree, size_t inlength) {
1544     //decode a single symbol from given list of bits with given code tree. return value is the symbol
1545     bool decoded; unsigned long ct;
1546     for(size_t treepos = 0;;) {
1547       if((bp & 0x07) == 0 && (bp >> 3) > inlength) { error = 10; return 0; } //error: end reached without endcode
1548       error = tree.decode(decoded, ct, treepos, readBitFromStream(bp, in));
1549       if(error) return 0; //stop, an error happened
1550       if(decoded) return ct;
1551     }
1552   }
1553 
getTreeInflateDynamiclodepng::ExtractZlib1554   void getTreeInflateDynamic(HuffmanTree& tree, HuffmanTree& treeD,
1555                              const unsigned char* in, size_t& bp, size_t inlength) {
1556     size_t bpstart = bp;
1557     //get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree
1558     std::vector<unsigned long> bitlen(288, 0), bitlenD(32, 0);
1559     if(bp >> 3 >= inlength - 2) { error = 49; return; } //the bit pointer is or will go past the memory
1560     size_t HLIT =  readBitsFromStream(bp, in, 5) + 257; //number of literal/length codes + 257
1561     size_t HDIST = readBitsFromStream(bp, in, 5) + 1; //number of dist codes + 1
1562     size_t HCLEN = readBitsFromStream(bp, in, 4) + 4; //number of code length codes + 4
1563     zlibinfo->back().hlit = HLIT - 257;
1564     zlibinfo->back().hdist = HDIST - 1;
1565     zlibinfo->back().hclen = HCLEN - 4;
1566     std::vector<unsigned long> codelengthcode(19); //lengths of tree to decode the lengths of the dynamic tree
1567     for(size_t i = 0; i < 19; i++) codelengthcode[CLCL[i]] = (i < HCLEN) ? readBitsFromStream(bp, in, 3) : 0;
1568     //code length code lengths
1569     for(size_t i = 0; i < codelengthcode.size(); i++) zlibinfo->back().clcl.push_back(codelengthcode[i]);
1570     error = codelengthcodetree.makeFromLengths(codelengthcode, 7); if(error) return;
1571     size_t i = 0, replength;
1572     while(i < HLIT + HDIST) {
1573       unsigned long code = huffmanDecodeSymbol(in, bp, codelengthcodetree, inlength); if(error) return;
1574       zlibinfo->back().treecodes.push_back(code); //tree symbol code
1575       if(code <= 15)  { if(i < HLIT) bitlen[i++] = code; else bitlenD[i++ - HLIT] = code; } //a length code
1576       else if(code == 16) { //repeat previous
1577         if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory
1578         replength = 3 + readBitsFromStream(bp, in, 2);
1579         unsigned long value; //set value to the previous code
1580         if((i - 1) < HLIT) value = bitlen[i - 1];
1581         else value = bitlenD[i - HLIT - 1];
1582         for(size_t n = 0; n < replength; n++) { //repeat this value in the next lengths
1583           if(i >= HLIT + HDIST) { error = 13; return; } //error: i is larger than the amount of codes
1584           if(i < HLIT) bitlen[i++] = value; else bitlenD[i++ - HLIT] = value;
1585         }
1586       } else if(code == 17) { //repeat "0" 3-10 times
1587         if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory
1588         replength = 3 + readBitsFromStream(bp, in, 3);
1589         zlibinfo->back().treecodes.push_back(replength); //tree symbol code repetitions
1590         for(size_t n = 0; n < replength; n++) { //repeat this value in the next lengths
1591           if(i >= HLIT + HDIST) { error = 14; return; } //error: i is larger than the amount of codes
1592           if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0;
1593         }
1594       } else if(code == 18) { //repeat "0" 11-138 times
1595         if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory
1596         replength = 11 + readBitsFromStream(bp, in, 7);
1597         zlibinfo->back().treecodes.push_back(replength); //tree symbol code repetitions
1598         for(size_t n = 0; n < replength; n++) { //repeat this value in the next lengths
1599           if(i >= HLIT + HDIST) { error = 15; return; } //error: i is larger than the amount of codes
1600           if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0;
1601         }
1602       }
1603       else { error = 16; return; } //error: somehow an unexisting code appeared. This can never happen.
1604     }
1605     if(bitlen[256] == 0) { error = 64; return; } //the length of the end code 256 must be larger than 0
1606     error = tree.makeFromLengths(bitlen, 15);
1607     if(error) return; //now we've finally got HLIT and HDIST, so generate the code trees, and the function is done
1608     error = treeD.makeFromLengths(bitlenD, 15);
1609     if(error) return;
1610     zlibinfo->back().treebits = bp - bpstart;
1611     //lit/len/end symbol lengths
1612     for(size_t j = 0; j < bitlen.size(); j++) zlibinfo->back().litlenlengths.push_back(bitlen[j]);
1613     //dist lengths
1614     for(size_t j = 0; j < bitlenD.size(); j++) zlibinfo->back().distlengths.push_back(bitlenD[j]);
1615   }
1616 
inflateHuffmanBlocklodepng::ExtractZlib1617   void inflateHuffmanBlock(std::vector<unsigned char>& out,
1618                            const unsigned char* in, size_t& bp, size_t& pos, size_t inlength, unsigned long btype) {
1619     size_t numcodes = 0, numlit = 0, numlen = 0; //for logging
1620     if(btype == 1) { generateFixedTrees(codetree, codetreeD); }
1621     else if(btype == 2) { getTreeInflateDynamic(codetree, codetreeD, in, bp, inlength); if(error) return; }
1622     for(;;) {
1623       unsigned long code = huffmanDecodeSymbol(in, bp, codetree, inlength); if(error) return;
1624       numcodes++;
1625       zlibinfo->back().lz77_lcode.push_back(code); //output code
1626       zlibinfo->back().lz77_dcode.push_back(0);
1627       zlibinfo->back().lz77_lbits.push_back(0);
1628       zlibinfo->back().lz77_dbits.push_back(0);
1629       zlibinfo->back().lz77_lvalue.push_back(0);
1630       zlibinfo->back().lz77_dvalue.push_back(0);
1631 
1632       if(code == 256) {
1633         break; //end code
1634       } else if(code <= 255) { //literal symbol
1635         out.push_back((unsigned char)(code));
1636         pos++;
1637         numlit++;
1638       } else if(code >= 257 && code <= 285) { //length code
1639         size_t length = LENBASE[code - 257], numextrabits = LENEXTRA[code - 257];
1640         if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory
1641         length += readBitsFromStream(bp, in, numextrabits);
1642         unsigned long codeD = huffmanDecodeSymbol(in, bp, codetreeD, inlength); if(error) return;
1643         if(codeD > 29) { error = 18; return; } //error: invalid dist code (30-31 are never used)
1644         unsigned long dist = DISTBASE[codeD], numextrabitsD = DISTEXTRA[codeD];
1645         if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory
1646         dist += readBitsFromStream(bp, in, numextrabitsD);
1647         size_t start = pos, back = start - dist; //backwards
1648         for(size_t i = 0; i < length; i++) {
1649           out.push_back(out[back++]);
1650           pos++;
1651           if(back >= start) back = start - dist;
1652         }
1653         numlen++;
1654         zlibinfo->back().lz77_dcode.back() = codeD; //output distance code
1655         zlibinfo->back().lz77_lbits.back() = numextrabits; //output length extra bits
1656         zlibinfo->back().lz77_dbits.back() = numextrabitsD; //output dist extra bits
1657         zlibinfo->back().lz77_lvalue.back() = length; //output length
1658         zlibinfo->back().lz77_dvalue.back() = dist; //output dist
1659       }
1660     }
1661     zlibinfo->back().numlit = numlit; //output number of literal symbols
1662     zlibinfo->back().numlen = numlen; //output number of length symbols
1663   }
1664 
inflateNoCompressionlodepng::ExtractZlib1665   void inflateNoCompression(std::vector<unsigned char>& out,
1666                             const unsigned char* in, size_t& bp, size_t& pos, size_t inlength) {
1667     while((bp & 0x7) != 0) bp++; //go to first boundary of byte
1668     size_t p = bp / 8;
1669     if(p >= inlength - 4) { error = 52; return; } //error, bit pointer will jump past memory
1670     unsigned long LEN = in[p] + 256u * in[p + 1], NLEN = in[p + 2] + 256u * in[p + 3]; p += 4;
1671     if(LEN + NLEN != 65535) { error = 21; return; } //error: NLEN is not one's complement of LEN
1672     if(p + LEN > inlength) { error = 23; return; } //error: reading outside of in buffer
1673     for(unsigned long n = 0; n < LEN; n++) {
1674       out.push_back(in[p++]); //read LEN bytes of literal data
1675       pos++;
1676     }
1677     bp = p * 8;
1678   }
1679 
decompresslodepng::ExtractZlib1680   int decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in) { //returns error value
1681     if(in.size() < 2) { return 53; } //error, size of zlib data too small
1682     //error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way
1683     if((in[0] * 256 + in[1]) % 31 != 0) { return 24; }
1684     unsigned long CM = in[0] & 15, CINFO = (in[0] >> 4) & 15, FDICT = (in[1] >> 5) & 1;
1685     //error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec
1686     if(CM != 8 || CINFO > 7) { return 25; }
1687     //error: the PNG spec says about the zlib stream: "The additional flags shall not specify a preset dictionary."
1688     if(FDICT != 0) { return 26; }
1689     inflate(out, in, 2);
1690     return error; //note: adler32 checksum was skipped and ignored
1691   }
1692 };
1693 
1694 struct ExtractPNG { //PNG decoding and information extraction
1695   std::vector<ZlibBlockInfo>* zlibinfo;
ExtractPNGlodepng::ExtractPNG1696   ExtractPNG(std::vector<ZlibBlockInfo>* info) : zlibinfo(info) {};
1697   int error;
decodelodepng::ExtractPNG1698   void decode(const unsigned char* in, size_t size) {
1699     error = 0;
1700     if(size == 0 || in == 0) { error = 48; return; } //the given data is empty
1701     readPngHeader(&in[0], size); if(error) return;
1702     size_t pos = 33; //first byte of the first chunk after the header
1703     std::vector<unsigned char> idat; //the data from idat chunks
1704     bool IEND = false;
1705     //loop through the chunks, ignoring unknown chunks and stopping at IEND chunk.
1706     //IDAT data is put at the start of the in buffer
1707     while(!IEND) {
1708       //error: size of the in buffer too small to contain next chunk
1709       if(pos + 8 >= size) { error = 30; return; }
1710       size_t chunkLength = read32bitInt(&in[pos]); pos += 4;
1711       if(chunkLength > 2147483647) { error = 63; return; }
1712       //error: size of the in buffer too small to contain next chunk
1713       if(pos + chunkLength >= size) { error = 35; return; }
1714       //IDAT chunk, containing compressed image data
1715       if(in[pos + 0] == 'I' && in[pos + 1] == 'D' && in[pos + 2] == 'A' && in[pos + 3] == 'T') {
1716         idat.insert(idat.end(), &in[pos + 4], &in[pos + 4 + chunkLength]);
1717         pos += (4 + chunkLength);
1718       } else if(in[pos + 0] == 'I' && in[pos + 1] == 'E' && in[pos + 2] == 'N' && in[pos + 3] == 'D') {
1719           pos += 4;
1720           IEND = true;
1721       } else { //it's not an implemented chunk type, so ignore it: skip over the data
1722         pos += (chunkLength + 4); //skip 4 letters and uninterpreted data of unimplemented chunk
1723       }
1724       pos += 4; //step over CRC (which is ignored)
1725     }
1726     std::vector<unsigned char> out; //now the out buffer will be filled
1727     ExtractZlib zlib(zlibinfo); //decompress with the Zlib decompressor
1728     error = zlib.decompress(out, idat);
1729     if(error) return; //stop if the zlib decompressor returned an error
1730   }
1731 
1732   //read the information from the header and store it in the Info
readPngHeaderlodepng::ExtractPNG1733   void readPngHeader(const unsigned char* in, size_t inlength) {
1734     if(inlength < 29) { error = 27; return; } //error: the data length is smaller than the length of the header
1735     if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71
1736     || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { error = 28; return; } //no PNG signature
1737     //error: it doesn't start with a IHDR chunk!
1738     if(in[12] != 'I' || in[13] != 'H' || in[14] != 'D' || in[15] != 'R') { error = 29; return; }
1739   }
1740 
readBitFromReversedStreamlodepng::ExtractPNG1741   unsigned long readBitFromReversedStream(size_t& bitp, const unsigned char* bits) {
1742     unsigned long result = (bits[bitp >> 3] >> (7 - (bitp & 0x7))) & 1;
1743     bitp++;
1744     return result;
1745   }
1746 
readBitsFromReversedStreamlodepng::ExtractPNG1747   unsigned long readBitsFromReversedStream(size_t& bitp, const unsigned char* bits, unsigned long nbits) {
1748     unsigned long result = 0;
1749     for(size_t i = nbits - 1; i < nbits; i--) result += ((readBitFromReversedStream(bitp, bits)) << i);
1750     return result;
1751   }
1752 
setBitOfReversedStreamlodepng::ExtractPNG1753   void setBitOfReversedStream(size_t& bitp, unsigned char* bits, unsigned long bit) {
1754     bits[bitp >> 3] |=  (bit << (7 - (bitp & 0x7))); bitp++;
1755   }
1756 
read32bitIntlodepng::ExtractPNG1757   unsigned long read32bitInt(const unsigned char* buffer) {
1758     return (unsigned int)((buffer[0] << 24u) | (buffer[1] << 16u) | (buffer[2] << 8u) | buffer[3]);
1759   }
1760 };
1761 
extractZlibInfo(std::vector<ZlibBlockInfo> & zlibinfo,const std::vector<unsigned char> & in)1762 void extractZlibInfo(std::vector<ZlibBlockInfo>& zlibinfo, const std::vector<unsigned char>& in) {
1763   ExtractPNG decoder(&zlibinfo);
1764   decoder.decode(&in[0], in.size());
1765 
1766   if(decoder.error) std::cout << "extract error: " << decoder.error << std::endl;
1767 }
1768 
1769 } // namespace lodepng
1770