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