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