1 /*
2 LodePNG Unit Test
3 
4 Copyright (c) 2005-2019 Lode Vandevenne
5 
6 This software is provided 'as-is', without any express or implied
7 warranty. In no event will the authors be held liable for any damages
8 arising from the use of this software.
9 
10 Permission is granted to anyone to use this software for any purpose,
11 including commercial applications, and to alter it and redistribute it
12 freely, subject to the following restrictions:
13 
14     1. The origin of this software must not be misrepresented; you must not
15     claim that you wrote the original software. If you use this software
16     in a product, an acknowledgment in the product documentation would be
17     appreciated but is not required.
18 
19     2. Altered source versions must be plainly marked as such, and must not be
20     misrepresented as being the original software.
21 
22     3. This notice may not be removed or altered from any source
23     distribution.
24 */
25 
26 //g++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wsign-conversion -pedantic -ansi -O3
27 
28 /*
29 Testing instructions:
30 
31 *) Ensure no tests commented out below or early return in doMain
32 
33 *) Compile with g++ with all warnings and run the unit test
34 g++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wsign-conversion -Wshadow -pedantic -ansi -O3 && ./a.out
35 
36 *) Compile with clang, which may sometimes give different warnings
37 clang++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wsign-conversion -Wshadow -pedantic -ansi -O3
38 
39 *) Compile with pure ISO C90 and all warnings:
40 mv lodepng.cpp lodepng.c ; gcc -I ./ lodepng.c examples/example_decode.c -ansi -pedantic -Wall -Wextra -O3 ; mv lodepng.c lodepng.cpp
41 
42 mv lodepng.cpp lodepng.c ; clang -I ./ lodepng.c examples/example_decode.c -ansi -pedantic -Wall -Wextra -O3 ; mv lodepng.c lodepng.cpp
43 
44 *) Compile with C with -pedantic but not -ansi flag so it warns about // style comments in C++-only ifdefs
45 mv lodepng.cpp lodepng.c ; gcc -I ./ lodepng.c examples/example_decode.c -pedantic -Wall -Wextra -O3 ; mv lodepng.c lodepng.cpp
46 
47 *) try lodepng_benchmark.cpp
48 g++ lodepng.cpp lodepng_benchmark.cpp -Wall -Wextra -pedantic -ansi -lSDL -O3 && ./a.out testdata/corpus/''*
49 
50 *) try the fuzzer
51 clang++ -fsanitize=fuzzer lodepng.cpp lodepng_fuzzer.cpp -O3 && ./a.out
52 
53 *) Check if all C++ examples compile without warnings:
54 g++ -I ./ lodepng.cpp examples/''*.cpp -W -Wall -ansi -pedantic -O3 -c
55 
56 *) Check if all C examples compile without warnings:
57 mv lodepng.cpp lodepng.c ; gcc -I ./ lodepng.c examples/''*.c -W -Wall -ansi -pedantic -O3 -c ; mv lodepng.c lodepng.cpp
58 
59 *) Check pngdetail.cpp:
60 g++ lodepng.cpp lodepng_util.cpp pngdetail.cpp -W -Wall -ansi -pedantic -O3 -o pngdetail
61 ./pngdetail testdata/PngSuite/basi0g01.png
62 
63 *) Test compiling with some code sections with #defines disabled, for unused static function warnings etc...
64 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ZLIB
65 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_PNG
66 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_DECODER
67 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ENCODER
68 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_DISK
69 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
70 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ERROR_TEXT
71 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_CPP
72 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ZLIB -DLODEPNG_NO_COMPILE_DECODER
73 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ZLIB -DLODEPNG_NO_COMPILE_ENCODER
74 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_PNG -DLODEPNG_NO_COMPILE_DECODER
75 g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_PNG -DLODEPNG_NO_COMPILE_ENCODER
76 rm *.o
77 
78 *) analyze with clang:
79 clang++ lodepng.cpp --analyze
80 
81 More verbose:
82 clang++ --analyze -Xanalyzer -analyzer-output=text lodepng.cpp
83 
84 Or html, look under lodepng.plist dir afterwards and find the numbered locations in the pages:
85 clang++ --analyze -Xanalyzer -analyzer-output=html lodepng.cpp
86 
87 *) check for memory leaks and vulnerabilities with valgrind
88 (DISABLE_SLOW disables a few tests that are very slow with valgrind)
89 g++ -DDISABLE_SLOW lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -pedantic -ansi -O3 -DLODEPNG_MAX_ALLOC=100000000 && valgrind --leak-check=full --track-origins=yes ./a.out
90 
91 *) Try with clang++ and address sanitizer (to get line numbers, make sure 'llvm' is also installed to get 'llvm-symbolizer'
92 clang++ -O3 -fsanitize=address lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wshadow -pedantic -ansi && ASAN_OPTIONS=allocator_may_return_null=1 ./a.out
93 
94 clang++ -g3 -fsanitize=address lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wshadow -pedantic -ansi && ASAN_OPTIONS=allocator_may_return_null=1 ./a.out
95 
96 *) Idem for undefined behavior
97 clang++ -O3 -fsanitize=undefined lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wshadow -pedantic -ansi && ASAN_OPTIONS=allocator_may_return_null=1 ./a.out
98 
99 *) remove "#include <iostream>" from lodepng.cpp if it's still in there (some are legit)
100 cat lodepng.cpp lodepng_util.cpp | grep iostream
101 cat lodepng.cpp lodepng_util.cpp | grep stdio
102 cat lodepng.cpp lodepng_util.cpp | grep "#include"
103 
104 *) try the Makefile
105 make clean && make -j
106 
107 *) check that no plain "free", "malloc", "realloc", "strlen", "memcpy", ... used, but the lodepng_* versions instead
108 
109 *) check version dates in copyright message and LODEPNG_VERSION_STRING
110 
111 *) check year in copyright message at top of all files as well as at bottom of lodepng.h
112 
113 *) check examples/sdl.cpp with the png test suite images (the "x" ones are expected to show error)
114 g++ -I ./ lodepng.cpp examples/example_sdl.cpp -Wall -Wextra -pedantic -ansi -O3 -lSDL -o showpng && ./showpng testdata/PngSuite/''*.png
115 
116 *) strip trailing spaces and ensure consistent newlines
117 
118 *) check diff of lodepng.cpp and lodepng.h before submitting
119 git difftool -y
120 
121 */
122 
123 #include "lodepng.h"
124 #include "lodepng_util.h"
125 
126 #include <cmath>
127 #include <map>
128 #include <iomanip>
129 #include <iostream>
130 #include <sstream>
131 #include <string>
132 #include <vector>
133 
134 #include <stdio.h>
135 #include <stdlib.h>
136 
137 ////////////////////////////////////////////////////////////////////////////////
138 
fail()139 void fail() {
140   throw 1; //that's how to let a unittest fail
141 }
142 
143 //Utility for debug messages
144 template<typename T>
valtostr(const T & val)145 std::string valtostr(const T& val) {
146   std::ostringstream sstream;
147   sstream << val;
148   return sstream.str();
149 }
150 
151 //Print char as a numeric value rather than a character
152 template<>
valtostr(const unsigned char & val)153 std::string valtostr(const unsigned char& val) {
154   std::ostringstream sstream;
155   sstream << (int)val;
156   return sstream.str();
157 }
158 
159 //Print char pointer as pointer, not as string
160 template<typename T>
valtostr(const T * val)161 std::string valtostr(const T* val) {
162   std::ostringstream sstream;
163   sstream << (const void*)val;
164   return sstream.str();
165 }
166 
167 template<typename T>
valtostr(const std::vector<T> & val)168 std::string valtostr(const std::vector<T>& val) {
169   std::ostringstream sstream;
170   sstream << "[vector with size " << val.size() << "]";
171   return sstream.str();
172 }
173 
174 // TODO: remove, use only ASSERT_EQUALS (it prints line number). Requires adding extra message ability to ASSERT_EQUALS
175 template<typename T, typename U>
assertEquals(const T & expected,const U & actual,const std::string & message="")176 void assertEquals(const T& expected, const U& actual, const std::string& message = "") {
177   if(expected != (T)actual) {
178     std::cout << "Error: Not equal! Expected " << valtostr(expected)
179               << " got " << valtostr((T)actual) << ". "
180               << "Message: " << message << std::endl;
181     fail();
182   }
183 }
184 
185 // TODO: turn into ASSERT_TRUE with line number printed
assertTrue(bool value,const std::string & message="")186 void assertTrue(bool value, const std::string& message = "") {
187   if(!value) {
188     std::cout << "Error: expected true. " << "Message: " << message << std::endl;
189     fail();
190   }
191 }
192 
193 //assert that no error
assertNoPNGError(unsigned error,const std::string & message="")194 void assertNoPNGError(unsigned error, const std::string& message = "") {
195   if(error) {
196     std::string msg = (message == "") ? lodepng_error_text(error)
197                                       : message + std::string(": ") + lodepng_error_text(error);
198     assertEquals(0, error, msg);
199   }
200 }
201 
assertNoError(unsigned error)202 void assertNoError(unsigned error) {
203   if(error) {
204     assertEquals(0, error, "Expected no error");
205   }
206 }
207 
208 #define STR_EXPAND(s) #s
209 #define STR(s) STR_EXPAND(s)
210 #define ASSERT_EQUALS(e, v) {\
211   if((e) != (v)) {\
212     std::cout << std::string("line ") + STR(__LINE__) + ": " + STR(v) + " ASSERT_EQUALS failed: ";\
213     std::cout << "Expected " << valtostr(e) << " but got " << valtostr(v) << ". " << std::endl;\
214     fail();\
215   }\
216 }
217 #define ASSERT_NOT_EQUALS(e, v) {\
218   if((e) == (v)) {\
219     std::cout << std::string("line ") + STR(__LINE__) + ": " + STR(v) + " ASSERT_NOT_EQUALS failed: ";\
220     std::cout << "Expected not " << valtostr(e) << " but got " << valtostr(v) << ". " << std::endl;\
221     fail();\
222   }\
223 }
224 
225 template<typename T, typename U, typename V>
isNear(T e,U v,V maxdist)226 bool isNear(T e, U v, V maxdist) {
227   T dist = e > (T)v ? e - (T)v : (T)v - e;
228   return dist <= (T)maxdist;
229 }
230 
231 template<typename T, typename U>
232 T diff(T e, U v) {
233   return v > e ? v - e : e - v;
234 }
235 
236 #define ASSERT_NEAR(e, v, maxdist) {\
237   if(!isNear(e, v, maxdist)) {\
238     std::cout << std::string("line ") + STR(__LINE__) + ": " + STR(v) + " ASSERT_NEAR failed: ";\
239     std::cout << "dist too great! Expected near " << valtostr(e) << " but got " << valtostr(v) << ", with max dist " << valtostr(maxdist)\
240               << " but got dist " << valtostr(diff(e, v)) << ". " << std::endl;\
241     fail();\
242   }\
243 }
244 
245 #define ASSERT_STRING_EQUALS(e, v) ASSERT_EQUALS(std::string(e), std::string(v))
246 #define ASSERT_NO_PNG_ERROR_MSG(error, message) assertNoPNGError(error, std::string("line ") + STR(__LINE__) + (std::string(message).empty() ? std::string("") : (": " + std::string(message))))
247 #define ASSERT_NO_PNG_ERROR(error) ASSERT_NO_PNG_ERROR_MSG(error, std::string(""))
248 
249 static const std::string BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
250 
251 
252 
253 //T and U can be std::string or std::vector<unsigned char>
254 template<typename T, typename U>
toBase64(T & out,const U & in)255 void toBase64(T& out, const U& in) {
256   for(size_t i = 0; i < in.size(); i += 3) {
257     int v = 65536 * in[i];
258     if(i + 1 < in.size()) v += 256 * in[i + 1];
259     if(i + 2 < in.size()) v += in[i + 2];
260     out.push_back(BASE64[(v >> 18) & 0x3f]);
261     out.push_back(BASE64[(v >> 12) & 0x3f]);
262     if(i + 1 < in.size()) out.push_back(BASE64[(v >> 6) & 0x3f]);
263     else out.push_back('=');
264     if(i + 2 < in.size()) out.push_back(BASE64[(v >> 0) & 0x3f]);
265     else out.push_back('=');
266   }
267 }
268 
fromBase64(int v)269 int fromBase64(int v) {
270   if(v >= 'A' && v <= 'Z') return (v - 'A');
271   if(v >= 'a' && v <= 'z') return (v - 'a' + 26);
272   if(v >= '0' && v <= '9') return (v - '0' + 52);
273   if(v == '+') return 62;
274   if(v == '/') return 63;
275   return 0; //v == '='
276 }
277 
278 //T and U can be std::string or std::vector<unsigned char>
279 template<typename T, typename U>
fromBase64(T & out,const U & in)280 void fromBase64(T& out, const U& in) {
281   for(size_t i = 0; i + 3 < in.size(); i += 4) {
282     int v = 262144 * fromBase64(in[i]) + 4096 * fromBase64(in[i + 1]) + 64 * fromBase64(in[i + 2]) + fromBase64(in[i + 3]);
283     out.push_back((v >> 16) & 0xff);
284     if(in[i + 2] != '=') out.push_back((v >> 8) & 0xff);
285     if(in[i + 3] != '=') out.push_back((v >> 0) & 0xff);
286   }
287 }
288 
getRandom()289 unsigned getRandom() {
290   static unsigned s = 1000000000;
291   // xorshift32, good enough for testing
292   s ^= (s << 13);
293   s ^= (s >> 17);
294   s ^= (s << 5);
295   return s;
296 }
297 
298 ////////////////////////////////////////////////////////////////////////////////
299 
300 //Test image data
301 struct Image {
302   std::vector<unsigned char> data;
303   unsigned width;
304   unsigned height;
305   LodePNGColorType colorType;
306   unsigned bitDepth;
307 };
308 
309 //Get number of color channels for a given PNG color type
getNumColorChannels(unsigned colorType)310 unsigned getNumColorChannels(unsigned colorType) {
311   switch(colorType) {
312     case 0: return 1; /*gray*/
313     case 2: return 3; /*RGB*/
314     case 3: return 1; /*palette*/
315     case 4: return 2; /*gray + alpha*/
316     case 6: return 4; /*RGBA*/
317   }
318   return 0; /*unexisting color type*/
319 }
320 
321 //Generate a test image with some data in it, the contents of the data is unspecified,
322 //except the content is not just one plain color, and not true random either to be compressible.
generateTestImage(Image & image,unsigned width,unsigned height,LodePNGColorType colorType=LCT_RGBA,unsigned bitDepth=8)323 void generateTestImage(Image& image, unsigned width, unsigned height, LodePNGColorType colorType = LCT_RGBA, unsigned bitDepth = 8) {
324   image.width = width;
325   image.height = height;
326   image.colorType = colorType;
327   image.bitDepth = bitDepth;
328 
329   size_t bits = bitDepth * getNumColorChannels(colorType); //bits per pixel
330   size_t size = (width * height * bits + 7) / 8; //total image size in bytes
331   image.data.resize(size);
332   unsigned char value = 128;
333   for(size_t i = 0; i < size; i++) {
334     image.data[i] = value++;
335   }
336 }
337 
338 //Generate a 16-bit test image with minimal size that requires at minimum the given color type (bit depth, grayscaleness, ...)
339 //If key is true, makes it such that exactly one color is transparent, so it can use a key. If false, adds a translucent color depending on
340 //whether it's an alpha color type or not.
generateTestImageRequiringColorType16(Image & image,LodePNGColorType colorType,unsigned bitDepth,bool key)341 void generateTestImageRequiringColorType16(Image& image, LodePNGColorType colorType, unsigned bitDepth, bool key) {
342   image.colorType = colorType;
343   image.bitDepth = bitDepth;
344   unsigned w = 1;
345   unsigned h = 1;
346 
347   bool gray = colorType == LCT_GREY || colorType == LCT_GREY_ALPHA;
348   bool alpha = colorType == LCT_RGBA || colorType == LCT_GREY_ALPHA;
349 
350   if(colorType == LCT_PALETTE) {
351     w = 1u << bitDepth;
352     h = 256; // ensure it'll really choose palette, not omit it due to small image size
353     image.data.resize(w * h * 8);
354     for(size_t y = 0; y < h; y++) {
355       for(size_t x = 0; x < w; x++) {
356         size_t i = y * w * 8 + x * 8;
357         image.data[i + 0] = image.data[i + 1] = y;
358         image.data[i + 2] = image.data[i + 3] = 255;
359         image.data[i + 4] = image.data[i + 5] = 0;
360         image.data[i + 6] = image.data[i + 7] = (key && y == 0) ? 0 : 255;
361       }
362     }
363   } else if(bitDepth == 16) {
364     // one color suffices for this model. But add one more to support key.
365     w = 2;
366     image.data.resize(w * h * 8);
367     image.data[0] = 10; image.data[1] = 20;
368     image.data[2] = 10; image.data[3] = 20;
369     image.data[4] = gray ? 10 : 110; image.data[5] = gray ? 20 : 120;
370     image.data[6] = alpha ? 128 : 255; image.data[7] = alpha ? 20 : 255;
371 
372     image.data[8] = 40; image.data[9] = 50;
373     image.data[10] = 40; image.data[11] = 50;
374     image.data[12] = gray ? 40 : 140; image.data[13] = gray ? 50 : 150;
375     image.data[14] = key ? 0 : 255; image.data[15] = key ? 0 : 255;
376   } else if(gray) {
377     w = 2;
378     unsigned v = 255u / ((1u << bitDepth) - 1u); // value that forces at least this bitdepth
379     image.data.resize(w * h * 8);
380     image.data[0] = v; image.data[1] = v;
381     image.data[2] = v; image.data[3] = v;
382     image.data[4] = v; image.data[5] = v;
383     image.data[6] = alpha ? v : 255; image.data[7] = alpha ? v : 255;
384 
385     image.data[8] = image.data[9] = 0;
386     image.data[10] = image.data[11] = 0;
387     image.data[12] = image.data[13] = 0;
388     image.data[14] = image.data[15] = key ? 0 : 255;
389   } else {
390     // now it's RGB or RGBA with bitdepth 8
391     w = 257; // must have at least more than 256 colors so it won't use palette
392     image.data.resize(w * h * 8);
393     for(size_t y = 0; y < h; y++) {
394       for(size_t x = 0; x < w; x++) {
395         size_t i = y * w * 8 + x * 8;
396         image.data[i + 0] = image.data[i + 1] = i / 2;
397         image.data[i + 2] = image.data[i + 3] = i / 3;
398         image.data[i + 4] = image.data[i + 5] = i / 5;
399         image.data[i + 6] = image.data[i + 7] = (key && y == 0) ? 0 : (alpha ? i : 255);
400       }
401     }
402   }
403 
404   image.width = w;
405   image.height = h;
406 }
407 
408 //Generate a 8-bit test image with minimal size that requires at minimum the given color type (bit depth, grayscaleness, ...). bitDepth max 8 here.
409 //If key is true, makes it such that exactly one color is transparent, so it can use a key. If false, adds a translucent color depending on
410 //whether it's an alpha color type or not.
generateTestImageRequiringColorType8(Image & image,LodePNGColorType colorType,unsigned bitDepth,bool key)411 void generateTestImageRequiringColorType8(Image& image, LodePNGColorType colorType, unsigned bitDepth, bool key) {
412   image.colorType = colorType;
413   image.bitDepth = bitDepth;
414   unsigned w = 1;
415   unsigned h = 1;
416 
417   bool gray = colorType == LCT_GREY || colorType == LCT_GREY_ALPHA;
418   bool alpha = colorType == LCT_RGBA || colorType == LCT_GREY_ALPHA;
419 
420   if(colorType == LCT_PALETTE) {
421     w = 1u << bitDepth;
422     h = 256; // ensure it'll really choose palette, not omit it due to small image size
423     image.data.resize(w * h * 4);
424     for(size_t y = 0; y < h; y++) {
425       for(size_t x = 0; x < w; x++) {
426         size_t i = y * w * 4 + x * 4;
427         image.data[i + 0] = x;
428         image.data[i + 1] = 255;
429         image.data[i + 2] = 0;
430         image.data[i + 3] = (key && x == 0) ? 0 : 255;
431       }
432     }
433   } else if(gray) {
434     w = 2;
435     unsigned v = 255u / ((1u << bitDepth) - 1u); // value that forces at least this bitdepth
436     image.data.resize(w * h * 4);
437     image.data[0] = v;
438     image.data[1] = v;
439     image.data[2] = v;
440     image.data[3] = alpha ? v : 255;
441 
442     image.data[4] = 0;
443     image.data[5] = 0;
444     image.data[6] = 0;
445     image.data[7] = key ? 0 : 255;
446   } else {
447     // now it's RGB or RGBA with bitdepth 8
448     w = 257; // must have at least more than 256 colors so it won't use palette
449     image.data.resize(w * h * 4);
450     for(size_t y = 0; y < h; y++) {
451       for(size_t x = 0; x < w; x++) {
452         size_t i = y * w * 4 + x * 4;
453         image.data[i + 0] = i / 2;
454         image.data[i + 1] = i / 3;
455         image.data[i + 2] = i / 5;
456         image.data[i + 3] = (key && x == 0) ? 0 : (alpha ? i : 255);
457       }
458     }
459   }
460 
461   image.width = w;
462   image.height = h;
463 }
464 
465 //Check that the decoded PNG pixels are the same as the pixels in the image
assertPixels(Image & image,const unsigned char * decoded,const std::string & message)466 void assertPixels(Image& image, const unsigned char* decoded, const std::string& message) {
467   for(size_t i = 0; i < image.data.size(); i++) {
468     int byte_expected = image.data[i];
469     int byte_actual = decoded[i];
470 
471     //last byte is special due to possible random padding bits which need not to be equal
472     if(i == image.data.size() - 1) {
473       size_t numbits = getNumColorChannels(image.colorType) * image.bitDepth * image.width * image.height;
474       size_t padding = 8u - (numbits - 8u * (numbits / 8u));
475       if(padding != 8u) {
476         //set all padding bits of both to 0
477         for(size_t j = 0; j < padding; j++) {
478           byte_expected = (byte_expected & (~(1 << j))) % 256;
479           byte_actual = (byte_actual & (~(1 << j))) % 256;
480         }
481       }
482     }
483 
484     assertEquals(byte_expected, byte_actual, message + " " + valtostr(i));
485   }
486 }
487 
488 //Test LodePNG encoding and decoding the encoded result, using the C interface
doCodecTestC(Image & image)489 void doCodecTestC(Image& image) {
490   unsigned char* encoded = 0;
491   size_t encoded_size = 0;
492   unsigned char* decoded = 0;
493   unsigned decoded_w;
494   unsigned decoded_h;
495 
496   struct OnExitScope {
497     unsigned char** a;
498     unsigned char** b;
499     OnExitScope(unsigned char** ca, unsigned char** cb) : a(ca), b(cb) {}
500     ~OnExitScope() { free(*a); free(*b); }
501   } onExitScope(&encoded, &decoded);
502 
503   unsigned error_enc = lodepng_encode_memory(&encoded, &encoded_size, &image.data[0],
504                                              image.width, image.height, image.colorType, image.bitDepth);
505 
506   if(error_enc != 0) std::cout << "Error: " << lodepng_error_text(error_enc) << std::endl;
507   ASSERT_NO_PNG_ERROR_MSG(error_enc, "encoder error C");
508 
509   //if the image is large enough, compressing it should result in smaller size
510   if(image.data.size() > 512) assertTrue(encoded_size < image.data.size(), "compressed size");
511 
512   unsigned error_dec = lodepng_decode_memory(&decoded, &decoded_w, &decoded_h,
513                                              encoded, encoded_size, image.colorType, image.bitDepth);
514 
515   if(error_dec != 0) std::cout << "Error: " << lodepng_error_text(error_dec) << std::endl;
516   ASSERT_NO_PNG_ERROR_MSG(error_dec, "decoder error C");
517 
518   ASSERT_EQUALS(image.width, decoded_w);
519   ASSERT_EQUALS(image.height, decoded_h);
520   assertPixels(image, decoded, "Pixels C");
521 }
522 
523 //Test LodePNG encoding and decoding the encoded result, using the C++ interface
doCodecTestCPP(Image & image)524 void doCodecTestCPP(Image& image) {
525   std::vector<unsigned char> encoded;
526   std::vector<unsigned char> decoded;
527   unsigned decoded_w;
528   unsigned decoded_h;
529 
530   unsigned error_enc = lodepng::encode(encoded, image.data, image.width, image.height,
531                                        image.colorType, image.bitDepth);
532 
533   ASSERT_NO_PNG_ERROR_MSG(error_enc, "encoder error C++");
534 
535   //if the image is large enough, compressing it should result in smaller size
536   if(image.data.size() > 512) assertTrue(encoded.size() < image.data.size(), "compressed size");
537 
538   unsigned error_dec = lodepng::decode(decoded, decoded_w, decoded_h, encoded, image.colorType, image.bitDepth);
539 
540   ASSERT_NO_PNG_ERROR_MSG(error_dec, "decoder error C++");
541 
542   ASSERT_EQUALS(image.width, decoded_w);
543   ASSERT_EQUALS(image.height, decoded_h);
544   ASSERT_EQUALS(image.data.size(), decoded.size());
545   assertPixels(image, &decoded[0], "Pixels C++");
546 }
547 
548 
doCodecTestWithEncState(Image & image,lodepng::State & state)549 void doCodecTestWithEncState(Image& image, lodepng::State& state) {
550   std::vector<unsigned char> encoded;
551   std::vector<unsigned char> decoded;
552   unsigned decoded_w;
553   unsigned decoded_h;
554   state.info_raw.colortype = image.colorType;
555   state.info_raw.bitdepth = image.bitDepth;
556 
557 
558   unsigned error_enc = lodepng::encode(encoded, image.data, image.width, image.height, state);
559   ASSERT_NO_PNG_ERROR_MSG(error_enc, "encoder error uncompressed");
560 
561   unsigned error_dec = lodepng::decode(decoded, decoded_w, decoded_h, encoded, image.colorType, image.bitDepth);
562 
563   ASSERT_NO_PNG_ERROR_MSG(error_dec, "decoder error uncompressed");
564 
565   ASSERT_EQUALS(image.width, decoded_w);
566   ASSERT_EQUALS(image.height, decoded_h);
567   ASSERT_EQUALS(image.data.size(), decoded.size());
568   assertPixels(image, &decoded[0], "Pixels uncompressed");
569 }
570 
571 
572 //Test LodePNG encoding and decoding the encoded result, using the C++ interface
doCodecTestUncompressed(Image & image)573 void doCodecTestUncompressed(Image& image) {
574   lodepng::State state;
575   state.encoder.zlibsettings.btype = 0;
576   doCodecTestWithEncState(image, state);
577 }
578 
doCodecTestNoLZ77(Image & image)579 void doCodecTestNoLZ77(Image& image) {
580   lodepng::State state;
581   state.encoder.zlibsettings.use_lz77 = 0;
582   doCodecTestWithEncState(image, state);
583 }
584 
585 //Test LodePNG encoding and decoding the encoded result, using the C++ interface, with interlace
doCodecTestInterlaced(Image & image)586 void doCodecTestInterlaced(Image& image) {
587   std::vector<unsigned char> encoded;
588   std::vector<unsigned char> decoded;
589   unsigned decoded_w;
590   unsigned decoded_h;
591 
592   lodepng::State state;
593   state.info_png.interlace_method = 1;
594   state.info_raw.colortype = image.colorType;
595   state.info_raw.bitdepth = image.bitDepth;
596 
597   unsigned error_enc = lodepng::encode(encoded, image.data, image.width, image.height, state);
598 
599   ASSERT_NO_PNG_ERROR_MSG(error_enc, "encoder error interlaced");
600 
601   //if the image is large enough, compressing it should result in smaller size
602   if(image.data.size() > 512) assertTrue(encoded.size() < image.data.size(), "compressed size");
603 
604   state.info_raw.colortype = image.colorType;
605   state.info_raw.bitdepth = image.bitDepth;
606   unsigned error_dec = lodepng::decode(decoded, decoded_w, decoded_h, state, encoded);
607 
608   ASSERT_NO_PNG_ERROR_MSG(error_dec, "decoder error interlaced");
609 
610   ASSERT_EQUALS(image.width, decoded_w);
611   ASSERT_EQUALS(image.height, decoded_h);
612   ASSERT_EQUALS(image.data.size(), decoded.size());
613   assertPixels(image, &decoded[0], "Pixels interlaced");
614 }
615 
616 //Test LodePNG encoding and decoding the encoded result
doCodecTest(Image & image)617 void doCodecTest(Image& image) {
618   doCodecTestC(image);
619   doCodecTestCPP(image);
620   doCodecTestInterlaced(image);
621   doCodecTestUncompressed(image);
622   doCodecTestNoLZ77(image);
623 }
624 
625 
626 //Test LodePNG encoding and decoding using some image generated with the given parameters
codecTest(unsigned width,unsigned height,LodePNGColorType colorType=LCT_RGBA,unsigned bitDepth=8)627 void codecTest(unsigned width, unsigned height, LodePNGColorType colorType = LCT_RGBA, unsigned bitDepth = 8) {
628   std::cout << "codec test " << width << " " << height << std::endl;
629   Image image;
630   generateTestImage(image, width, height, colorType, bitDepth);
631   doCodecTest(image);
632 }
633 
removeSpaces(const std::string & s)634 std::string removeSpaces(const std::string& s) {
635   std::string result;
636   for(size_t i = 0; i < s.size(); i++) if(s[i] != ' ') result += s[i];
637   return result;
638 }
639 
bitStringToBytes(std::vector<unsigned char> & bytes,const std::string & bits_)640 void bitStringToBytes(std::vector<unsigned char>& bytes, const std::string& bits_) {
641   std::string bits = removeSpaces(bits_);
642   bytes.resize((bits.size()) + 7 / 8);
643   for(size_t i = 0; i < bits.size(); i++) {
644     size_t j = i / 8;
645     size_t k = i % 8;
646     char c = bits[i];
647     if(k == 0) bytes[j] = 0;
648     if(c == '1') bytes[j] |= (1 << (7 - k));
649   }
650 }
651 
652 /*
653 test color convert on a single pixel. Testing palette and testing color keys is
654 not supported by this function. Pixel values given using bits in an std::string
655 of 0's and 1's.
656 */
colorConvertTest(const std::string & bits_in,LodePNGColorType colorType_in,unsigned bitDepth_in,const std::string & bits_out,LodePNGColorType colorType_out,unsigned bitDepth_out)657 void colorConvertTest(const std::string& bits_in, LodePNGColorType colorType_in, unsigned bitDepth_in,
658                       const std::string& bits_out, LodePNGColorType colorType_out, unsigned bitDepth_out) {
659   std::cout << "color convert test " << bits_in << " - " << bits_out << std::endl;
660 
661   std::vector<unsigned char> expected, actual, image;
662   bitStringToBytes(expected, bits_out);
663   actual.resize(expected.size());
664   bitStringToBytes(image, bits_in);
665   LodePNGColorMode mode_in, mode_out;
666   lodepng_color_mode_init(&mode_in);
667   lodepng_color_mode_init(&mode_out);
668   mode_in.colortype = colorType_in;
669   mode_in.bitdepth = bitDepth_in;
670   mode_out.colortype = colorType_out;
671   mode_out.bitdepth = bitDepth_out;
672   unsigned error = lodepng_convert(&actual[0], &image[0], &mode_out, &mode_in, 1, 1);
673 
674   ASSERT_NO_PNG_ERROR_MSG(error, "convert error");
675 
676   for(size_t i = 0; i < expected.size(); i++) {
677     assertEquals((int)expected[i], (int)actual[i], "byte " + valtostr(i));
678   }
679 
680   lodepng_color_mode_cleanup(&mode_in);
681   lodepng_color_mode_cleanup(&mode_out);
682 }
683 
testOtherPattern1()684 void testOtherPattern1() {
685   std::cout << "codec other pattern 1" << std::endl;
686 
687   Image image1;
688   size_t w = 192;
689   size_t h = 192;
690   image1.width = w;
691   image1.height = h;
692   image1.colorType = LCT_RGBA;
693   image1.bitDepth = 8;
694   image1.data.resize(w * h * 4u);
695   for(size_t y = 0; y < h; y++)
696   for(size_t x = 0; x < w; x++) {
697     //pattern 1
698     image1.data[4u * w * y + 4u * x + 0u] = (unsigned char)(127 * (1 + std::sin((                    x * x +                     y * y) / (w * h / 8.0))));
699     image1.data[4u * w * y + 4u * x + 1u] = (unsigned char)(127 * (1 + std::sin(((w - x - 1) * (w - x - 1) +                     y * y) / (w * h / 8.0))));
700     image1.data[4u * w * y + 4u * x + 2u] = (unsigned char)(127 * (1 + std::sin((                    x * x + (h - y - 1) * (h - y - 1)) / (w * h / 8.0))));
701     image1.data[4u * w * y + 4u * x + 3u] = (unsigned char)(127 * (1 + std::sin(((w - x - 1) * (w - x - 1) + (h - y - 1) * (h - y - 1)) / (w * h / 8.0))));
702   }
703 
704   doCodecTest(image1);
705 }
706 
testOtherPattern2()707 void testOtherPattern2() {
708   std::cout << "codec other pattern 2" << std::endl;
709 
710   Image image1;
711   size_t w = 192;
712   size_t h = 192;
713   image1.width = w;
714   image1.height = h;
715   image1.colorType = LCT_RGBA;
716   image1.bitDepth = 8;
717   image1.data.resize(w * h * 4u);
718   for(size_t y = 0; y < h; y++)
719   for(size_t x = 0; x < w; x++) {
720     image1.data[4u * w * y + 4u * x + 0u] = 255 * !(x & y);
721     image1.data[4u * w * y + 4u * x + 1u] = x ^ y;
722     image1.data[4u * w * y + 4u * x + 2u] = x | y;
723     image1.data[4u * w * y + 4u * x + 3u] = 255;
724   }
725 
726   doCodecTest(image1);
727 }
728 
testSinglePixel(int r,int g,int b,int a)729 void testSinglePixel(int r, int g, int b, int a) {
730   std::cout << "codec single pixel " << r << " " << g << " " << b << " " << a << std::endl;
731   Image pixel;
732   pixel.width = 1;
733   pixel.height = 1;
734   pixel.colorType = LCT_RGBA;
735   pixel.bitDepth = 8;
736   pixel.data.resize(4);
737   pixel.data[0] = r;
738   pixel.data[1] = g;
739   pixel.data[2] = b;
740   pixel.data[3] = a;
741 
742   doCodecTest(pixel);
743 }
744 
testColor(int r,int g,int b,int a)745 void testColor(int r, int g, int b, int a) {
746   std::cout << "codec test color " << r << " " << g << " " << b << " " << a << std::endl;
747   Image image;
748   image.width = 20;
749   image.height = 20;
750   image.colorType = LCT_RGBA;
751   image.bitDepth = 8;
752   image.data.resize(20 * 20 * 4);
753   for(size_t y = 0; y < 20; y++)
754   for(size_t x = 0; x < 20; x++) {
755     image.data[20 * 4 * y + 4 * x + 0] = r;
756     image.data[20 * 4 * y + 4 * x + 0] = g;
757     image.data[20 * 4 * y + 4 * x + 0] = b;
758     image.data[20 * 4 * y + 4 * x + 0] = a;
759   }
760 
761   doCodecTest(image);
762 
763   Image image2 = image;
764   image2.data[3] = 0; //one fully transparent pixel
765   doCodecTest(image2);
766   image2.data[3] = 128; //one semi transparent pixel
767   doCodecTest(image2);
768 
769   Image image3 = image;
770   // add 255 different colors
771   for(size_t i = 0; i < 255; i++) {
772     image.data[i * 4 + 0] = i;
773     image.data[i * 4 + 1] = i;
774     image.data[i * 4 + 2] = i;
775     image.data[i * 4 + 3] = 255;
776   }
777   doCodecTest(image3);
778   // a 256th color
779   image.data[255 * 4 + 0] = 255;
780   image.data[255 * 4 + 1] = 255;
781   image.data[255 * 4 + 2] = 255;
782   image.data[255 * 4 + 3] = 255;
783   doCodecTest(image3);
784 
785   testSinglePixel(r, g, b, a);
786 }
787 
788 // Tests combinations of various colors in different orders
testFewColors()789 void testFewColors() {
790   std::cout << "codec test few colors " << std::endl;
791   Image image;
792   image.width = 20;
793   image.height = 20;
794   image.colorType = LCT_RGBA;
795   image.bitDepth = 8;
796   image.data.resize(image.width * image.height * 4);
797   std::vector<unsigned char> colors;
798   colors.push_back(0); colors.push_back(0); colors.push_back(0); colors.push_back(255); // black
799   colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(255); // white
800   colors.push_back(128); colors.push_back(128); colors.push_back(128); colors.push_back(255); // gray
801   colors.push_back(0); colors.push_back(0); colors.push_back(255); colors.push_back(255); // blue
802   colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(0); // transparent white
803   colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(1); // translucent white
804   for(size_t i = 0; i < colors.size(); i += 4)
805   for(size_t j = 0; j < colors.size(); j += 4)
806   for(size_t k = 0; k < colors.size(); k += 4)
807   for(size_t l = 0; l < colors.size(); l += 4) {
808     //std::cout << (i/4) << " " << (j/4) << " " << (k/4) << " " << (l/4) << std::endl;
809     for(size_t c = 0; c < 4; c++) {
810       for(unsigned y = 0; y < image.height; y++)
811       for(unsigned x = 0; x < image.width; x++) {
812         image.data[y * image.width * 4 + x * 4 + c] = (x ^ y) ? colors[i + c] : colors[j + c];
813       }
814       image.data[c] = colors[k + c];
815       image.data[image.data.size() - 4 + c] = colors[l + c];
816     }
817     doCodecTest(image);
818   }
819 }
820 
testSize(unsigned w,unsigned h)821 void testSize(unsigned w, unsigned h) {
822   std::cout << "codec test size " << w << " " << h << std::endl;
823   Image image;
824   image.width = w;
825   image.height = h;
826   image.colorType = LCT_RGBA;
827   image.bitDepth = 8;
828   image.data.resize(w * h * 4);
829   for(size_t y = 0; y < h; y++)
830   for(size_t x = 0; x < w; x++) {
831     image.data[w * 4 * y + 4 * x + 0] = x % 256;
832     image.data[w * 4 * y + 4 * x + 0] = y % 256;
833     image.data[w * 4 * y + 4 * x + 0] = 255;
834     image.data[w * 4 * y + 4 * x + 0] = 255;
835   }
836 
837   doCodecTest(image);
838 }
839 
testPNGCodec()840 void testPNGCodec() {
841   codecTest(1, 1);
842   codecTest(2, 2);
843   codecTest(1, 1, LCT_GREY, 1);
844   codecTest(7, 7, LCT_GREY, 1);
845 #ifndef DISABLE_SLOW
846   codecTest(127, 127);
847   codecTest(127, 127, LCT_GREY, 1);
848   codecTest(500, 500);
849   codecTest(1, 10000);
850   codecTest(10000, 1);
851 
852   testOtherPattern1();
853   testOtherPattern2();
854 #endif // DISABLE_SLOW
855 
856   testColor(255, 255, 255, 255);
857   testColor(0, 0, 0, 255);
858   testColor(1, 2, 3, 255);
859   testColor(255, 0, 0, 255);
860   testColor(0, 255, 0, 255);
861   testColor(0, 0, 255, 255);
862   testColor(0, 0, 0, 255);
863   testColor(1, 1, 1, 255);
864   testColor(1, 1, 1, 1);
865   testColor(0, 0, 0, 128);
866   testColor(255, 0, 0, 128);
867   testColor(127, 127, 127, 255);
868   testColor(128, 128, 128, 255);
869   testColor(127, 127, 127, 128);
870   testColor(128, 128, 128, 128);
871   //transparent single pixels
872   testColor(0, 0, 0, 0);
873   testColor(255, 0, 0, 0);
874   testColor(1, 2, 3, 0);
875   testColor(255, 255, 255, 0);
876   testColor(254, 254, 254, 0);
877 
878   // This is mainly to test the Adam7 interlacing
879   for(unsigned h = 1; h < 12; h++)
880   for(unsigned w = 1; w < 12; w++) {
881     testSize(w, h);
882   }
883 }
884 
885 //Tests some specific color conversions with specific color bit combinations
testColorConvert()886 void testColorConvert() {
887   //test color conversions to RGBA8
888   colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111 11111111", LCT_RGBA, 8);
889   colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010 11111111", LCT_RGBA, 8);
890   colorConvertTest("1001", LCT_GREY, 4, "10011001 10011001 10011001 11111111", LCT_RGBA, 8);
891   colorConvertTest("10010101", LCT_GREY, 8, "10010101 10010101 10010101 11111111", LCT_RGBA, 8);
892   colorConvertTest("10010101 11111110", LCT_GREY_ALPHA, 8, "10010101 10010101 10010101 11111110", LCT_RGBA, 8);
893   colorConvertTest("10010101 00000001 11111110 00000001", LCT_GREY_ALPHA, 16, "10010101 10010101 10010101 11111110", LCT_RGBA, 8);
894   colorConvertTest("01010101 00000000 00110011", LCT_RGB, 8, "01010101 00000000 00110011 11111111", LCT_RGBA, 8);
895   colorConvertTest("01010101 00000000 00110011 10101010", LCT_RGBA, 8, "01010101 00000000 00110011 10101010", LCT_RGBA, 8);
896   colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011", LCT_RGB, 16, "10101010 11111111 11001100 11111111", LCT_RGBA, 8);
897   colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011 11100111 00011000", LCT_RGBA, 16, "10101010 11111111 11001100 11100111", LCT_RGBA, 8);
898 
899   //test color conversions to RGB8
900   colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111", LCT_RGB, 8);
901   colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010", LCT_RGB, 8);
902   colorConvertTest("1001", LCT_GREY, 4, "10011001 10011001 10011001", LCT_RGB, 8);
903   colorConvertTest("10010101", LCT_GREY, 8, "10010101 10010101 10010101", LCT_RGB, 8);
904   colorConvertTest("10010101 11111110", LCT_GREY_ALPHA, 8, "10010101 10010101 10010101", LCT_RGB, 8);
905   colorConvertTest("10010101 00000001 11111110 00000001", LCT_GREY_ALPHA, 16, "10010101 10010101 10010101", LCT_RGB, 8);
906   colorConvertTest("01010101 00000000 00110011", LCT_RGB, 8, "01010101 00000000 00110011", LCT_RGB, 8);
907   colorConvertTest("01010101 00000000 00110011 10101010", LCT_RGBA, 8, "01010101 00000000 00110011", LCT_RGB, 8);
908   colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011", LCT_RGB, 16, "10101010 11111111 11001100", LCT_RGB, 8);
909   colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011 11100111 00011000", LCT_RGBA, 16, "10101010 11111111 11001100", LCT_RGB, 8);
910 
911   //test color conversions to RGBA16
912   colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111", LCT_RGBA, 16);
913   colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010 10101010 10101010 10101010 11111111 11111111", LCT_RGBA, 16);
914 
915   //test grayscale color conversions
916   colorConvertTest("1", LCT_GREY, 1, "11111111", LCT_GREY, 8);
917   colorConvertTest("1", LCT_GREY, 1, "1111111111111111", LCT_GREY, 16);
918   colorConvertTest("0", LCT_GREY, 1, "00000000", LCT_GREY, 8);
919   colorConvertTest("0", LCT_GREY, 1, "0000000000000000", LCT_GREY, 16);
920   colorConvertTest("11", LCT_GREY, 2, "11111111", LCT_GREY, 8);
921   colorConvertTest("11", LCT_GREY, 2, "1111111111111111", LCT_GREY, 16);
922   colorConvertTest("10", LCT_GREY, 2, "10101010", LCT_GREY, 8);
923   colorConvertTest("10", LCT_GREY, 2, "1010101010101010", LCT_GREY, 16);
924   colorConvertTest("1000", LCT_GREY, 4, "10001000", LCT_GREY, 8);
925   colorConvertTest("1000", LCT_GREY, 4, "1000100010001000", LCT_GREY, 16);
926   colorConvertTest("10110101", LCT_GREY, 8, "1011010110110101", LCT_GREY, 16);
927   colorConvertTest("1011010110110101", LCT_GREY, 16, "10110101", LCT_GREY, 8);
928 
929   //others
930   colorConvertTest("11111111 11111111 11111111 00000000 00000000 00000000", LCT_RGB, 8, "10", LCT_GREY, 1);
931   colorConvertTest("11111111 11111111 11111111 11111111 11111111 11111111 00000000 00000000 00000000 00000000 00000000 00000000", LCT_RGB, 16, "10", LCT_GREY, 1);
932 }
933 
934 //This tests color conversions from any color model to any color model, with any bit depth
935 //But it tests only with colors black and white, because that are the only colors every single model supports
testColorConvert2()936 void testColorConvert2() {
937   std::cout << "testColorConvert2" << std::endl;
938   struct Combo {
939     LodePNGColorType colortype;
940     unsigned bitdepth;
941   };
942 
943   Combo combos[15] = { { LCT_GREY, 1}, { LCT_GREY, 2}, { LCT_GREY, 4}, { LCT_GREY, 8}, { LCT_GREY, 16}, { LCT_RGB, 8}, { LCT_RGB, 16}, { LCT_PALETTE, 1}, { LCT_PALETTE, 2}, { LCT_PALETTE, 4}, { LCT_PALETTE, 8}, { LCT_GREY_ALPHA, 8}, { LCT_GREY_ALPHA, 16}, { LCT_RGBA, 8}, { LCT_RGBA, 16},
944   };
945 
946   lodepng::State state;
947   LodePNGColorMode& mode_in = state.info_png.color;
948   LodePNGColorMode& mode_out = state.info_raw;
949   LodePNGColorMode mode_8;
950   lodepng_color_mode_init(&mode_8);
951 
952   for(size_t i = 0; i < 256; i++) {
953     size_t j = i == 1 ? 255 : i;
954     lodepng_palette_add(&mode_in, j, j, j, 255);
955     lodepng_palette_add(&mode_out, j, j, j, 255);
956   }
957 
958   for(size_t i = 0; i < 15; i++) {
959     mode_in.colortype = combos[i].colortype;
960     mode_in.bitdepth = combos[i].bitdepth;
961 
962     for(size_t j = 0; j < 15; j++) {
963       mode_out.colortype = combos[i].colortype;
964       mode_out.bitdepth = combos[i].bitdepth;
965 
966       unsigned char eight[36] = {
967           0,0,0,255, 255,255,255,255,
968           0,0,0,255, 255,255,255,255,
969           255,255,255,255, 0,0,0,255,
970           255,255,255,255, 255,255,255,255,
971           0,0,0,255 }; //input in RGBA8
972       unsigned char in[72]; //custom input color type
973       unsigned char out[72]; //custom output color type
974       unsigned char eight2[36]; //back in RGBA8 after all conversions to check correctness
975       unsigned error = 0;
976 
977       error |= lodepng_convert(in, eight, &mode_in, &mode_8, 3, 3);
978       if(!error) error |= lodepng_convert(out, in, &mode_out, &mode_in, 3, 3); //Test input to output type
979       if(!error) error |= lodepng_convert(eight2, out, &mode_8, &mode_out, 3, 3);
980 
981       if(!error) {
982         for(size_t k = 0; k < 36; k++) {
983           if(eight[k] != eight2[k]) {
984             error = 99999;
985             break;
986           }
987         }
988       }
989 
990       if(error) {
991         std::cout << "Error " << error << " i: " << i << " j: " << j
992           << " colortype i: " << combos[i].colortype
993           << " bitdepth i: " << combos[i].bitdepth
994           << " colortype j: " << combos[j].colortype
995           << " bitdepth j: " << combos[j].bitdepth
996           << std::endl;
997         if(error != 99999) ASSERT_NO_PNG_ERROR(error);
998         else fail();
999       }
1000     }
1001   }
1002 }
1003 
1004 //if compressible is true, the test will also assert that the compressed string is smaller
testCompressStringZlib(const std::string & text,bool compressible)1005 void testCompressStringZlib(const std::string& text, bool compressible) {
1006   if(text.size() < 500) std::cout << "compress test with text: " << text << std::endl;
1007   else std::cout << "compress test with text length: " << text.size() << std::endl;
1008 
1009   std::vector<unsigned char> in(text.size());
1010   for(size_t i = 0; i < text.size(); i++) in[i] = (unsigned char)text[i];
1011   unsigned char* out = 0;
1012   size_t outsize = 0;
1013   unsigned error = 0;
1014 
1015   error = lodepng_zlib_compress(&out, &outsize, in.empty() ? 0 : &in[0], in.size(), &lodepng_default_compress_settings);
1016   ASSERT_NO_PNG_ERROR(error);
1017   if(compressible) assertTrue(outsize < in.size());
1018 
1019   unsigned char* out2 = 0;
1020   size_t outsize2 = 0;
1021 
1022   error = lodepng_zlib_decompress(&out2, &outsize2, out, outsize, &lodepng_default_decompress_settings);
1023   ASSERT_NO_PNG_ERROR(error);
1024   ASSERT_EQUALS(outsize2, in.size());
1025   for(size_t i = 0; i < in.size(); i++) ASSERT_EQUALS(in[i], out2[i]);
1026 
1027   free(out);
1028   free(out2);
1029 }
1030 
testCompressZlib()1031 void testCompressZlib() {
1032   testCompressStringZlib("", false);
1033   testCompressStringZlib("a", false);
1034   testCompressStringZlib("aa", false);
1035   testCompressStringZlib("ababababababababababababababababababababababababababababababababababababababababababab", true);
1036   testCompressStringZlib("abaaaabaabbbaabbabbababbbbabababbbaabbbaaaabbbbabbbabbbaababbbbbaaabaabbabaaaabbbbbbab", true);
1037   testCompressStringZlib("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab", true);
1038   testCompressStringZlib("omnomnomnomnomnomnomnomnomnomnom", true);
1039   testCompressStringZlib("the quick brown fox jumps over the lazy dog. the quick brown fox jumps over the lazy dog.", true);
1040   testCompressStringZlib("abracadabra", false);
1041   testCompressStringZlib("hello hello hello hello hello hello hello hello hello hello hello?", true);
1042   testCompressStringZlib("WPgZX2D*um0H::,4/KU\"kt\"Ne\"#Qa.&#<aF9{jag]|{hv,IXez\
1043 \\DKn5zYdV{XxBi=n|1J-TwakWvp[b8|-kOcZ@QkAxJSMeZ0l&<*w0BP/CXM(LFH'", false);
1044   testCompressStringZlib("asdfhlkhfafsduyfbasiuytfgbiasuidygiausygdifaubsydfsdf", false);
1045   testCompressStringZlib("418541499849814614617987416457317375467441841687487", true);
1046   testCompressStringZlib("3.141592653589793238462643383279502884197169399375105820974944592307816406286", true);
1047   testCompressStringZlib("lodepng_zlib_decompress(&out2, &outsize2, out, outsize, &lodepng_default_decompress_settings);", true);
1048 }
1049 
testDiskCompressZlib(const std::string & filename)1050 void testDiskCompressZlib(const std::string& filename) {
1051   std::cout << "testDiskCompressZlib: File " << filename << std::endl;
1052 
1053   std::vector<unsigned char> buffer;
1054   lodepng::load_file(buffer, filename);
1055   std::string f;
1056   for(size_t i = 0; i < buffer.size(); i++) f += (char)buffer[i];
1057   testCompressStringZlib(f, false);
1058 }
1059 
testDiskPNG(const std::string & filename)1060 void testDiskPNG(const std::string& filename) {
1061   std::cout << "testDiskPNG: File " << filename << std::endl;
1062 
1063   Image image;
1064   image.colorType = LCT_RGB;
1065   image.bitDepth = 8;
1066   unsigned error = lodepng::decode(image.data, image.width, image.height, filename, image.colorType, image.bitDepth);
1067   ASSERT_NO_PNG_ERROR(error);
1068 
1069   doCodecTest(image);
1070 }
1071 
strtovector(const std::string & numbers)1072 std::vector<unsigned> strtovector(const std::string& numbers) {
1073   std::vector<unsigned> result;
1074   std::stringstream ss(numbers);
1075   unsigned i;
1076   while(ss >> i) result.push_back(i);
1077   return result;
1078 }
1079 
doTestHuffmanCodeLengths(const std::string & expectedstr,const std::string & counts,size_t bitlength)1080 void doTestHuffmanCodeLengths(const std::string& expectedstr, const std::string& counts, size_t bitlength) {
1081   std::vector<unsigned> expected = strtovector(expectedstr);
1082   std::vector<unsigned> count = strtovector(counts);
1083   std::cout << "doTestHuffmanCodeLengths: " << counts << std::endl;
1084   std::vector<unsigned> result(count.size());
1085   unsigned error = lodepng_huffman_code_lengths(&result[0], &count[0], count.size(), bitlength);
1086   ASSERT_NO_PNG_ERROR_MSG(error, "errorcode");
1087   std::stringstream ss1, ss2;
1088   for(size_t i = 0; i < count.size(); i++) {
1089     ss1 << expected[i] << " ";
1090     ss2 << result[i] << " ";
1091   }
1092   assertEquals(ss1.str(), ss2.str(), "value");
1093 }
1094 
testHuffmanCodeLengths()1095 void testHuffmanCodeLengths() {
1096   bool atleasttwo = true; //LodePNG generates at least two, instead of at least one, symbol
1097   if(atleasttwo) {
1098     doTestHuffmanCodeLengths("1 1", "0 0", 16);
1099     doTestHuffmanCodeLengths("1 1 0", "0 0 0", 16);
1100     doTestHuffmanCodeLengths("1 1", "1 0", 16);
1101     doTestHuffmanCodeLengths("1 1 0 0 0 0 0 0 0", "0 0 0 0 0 0 0 0 0", 16);
1102     doTestHuffmanCodeLengths("1 1 0 0 0 0 0 0 0", "1 0 0 0 0 0 0 0 0", 16);
1103     doTestHuffmanCodeLengths("1 1 0 0 0 0 0 0 0", "0 1 0 0 0 0 0 0 0", 16);
1104     doTestHuffmanCodeLengths("1 0 0 0 0 0 0 0 1", "0 0 0 0 0 0 0 0 1", 16);
1105     doTestHuffmanCodeLengths("0 0 0 0 0 0 0 1 1", "0 0 0 0 0 0 0 1 1", 16);
1106   } else {
1107     doTestHuffmanCodeLengths("1 0", "0 0", 16);
1108     doTestHuffmanCodeLengths("1 0 0", "0 0 0", 16);
1109     doTestHuffmanCodeLengths("1 0", "1 0", 16);
1110     doTestHuffmanCodeLengths("1", "1", 16);
1111     doTestHuffmanCodeLengths("1", "0", 16);
1112   }
1113   doTestHuffmanCodeLengths("1 1", "1 1", 16);
1114   doTestHuffmanCodeLengths("1 1", "1 100", 16);
1115   doTestHuffmanCodeLengths("2 2 1", "1 2 3", 16);
1116   doTestHuffmanCodeLengths("2 1 2", "2 3 1", 16);
1117   doTestHuffmanCodeLengths("1 2 2", "3 1 2", 16);
1118   doTestHuffmanCodeLengths("3 3 2 1", "1 30 31 32", 16);
1119   doTestHuffmanCodeLengths("2 2 2 2", "1 30 31 32", 2);
1120   doTestHuffmanCodeLengths("5 5 4 4 4 3 3 1", "1 2 3 4 5 6 7 500", 16);
1121 }
1122 
1123 /*
1124 Create a PNG image with all known chunks (except only one of tEXt or zTXt) plus
1125 unknown chunks, and a palette.
1126 */
createComplexPNG(std::vector<unsigned char> & png)1127 void createComplexPNG(std::vector<unsigned char>& png) {
1128   unsigned w = 16, h = 17;
1129   std::vector<unsigned char> image(w * h);
1130   for(size_t i = 0; i < w * h; i++) {
1131     image[i] = i % 256;
1132   }
1133 
1134   lodepng::State state;
1135   LodePNGInfo& info = state.info_png;
1136   info.color.colortype = LCT_PALETTE;
1137   info.color.bitdepth = 8;
1138   state.info_raw.colortype = LCT_PALETTE;
1139   state.info_raw.bitdepth = 8;
1140   state.encoder.auto_convert = false;
1141   state.encoder.text_compression = 1;
1142   state.encoder.add_id = 1;
1143   for(size_t i = 0; i < 256; i++) {
1144     lodepng_palette_add(&info.color, i, i, i, i);
1145     lodepng_palette_add(&state.info_raw, i, i, i, i);
1146   }
1147 
1148   info.background_defined = 1;
1149   info.background_r = 127;
1150 
1151   lodepng_add_text(&info, "key0", "string0");
1152   lodepng_add_text(&info, "key1", "string1");
1153 
1154   lodepng_add_itext(&info, "ikey0", "ilangtag0", "itranskey0", "istring0");
1155   lodepng_add_itext(&info, "ikey1", "ilangtag1", "itranskey1", "istring1");
1156 
1157   info.time_defined = 1;
1158   info.time.year = 2012;
1159   info.time.month = 1;
1160   info.time.day = 2;
1161   info.time.hour = 3;
1162   info.time.minute = 4;
1163   info.time.second = 5;
1164 
1165   info.phys_defined = 1;
1166   info.phys_x = 1;
1167   info.phys_y = 2;
1168   info.phys_unit = 1;
1169 
1170   lodepng_chunk_create(&info.unknown_chunks_data[0], &info.unknown_chunks_size[0], 3, "uNKa", (unsigned char*)"a00");
1171   lodepng_chunk_create(&info.unknown_chunks_data[0], &info.unknown_chunks_size[0], 3, "uNKa", (unsigned char*)"a01");
1172   lodepng_chunk_create(&info.unknown_chunks_data[1], &info.unknown_chunks_size[1], 3, "uNKb", (unsigned char*)"b00");
1173   lodepng_chunk_create(&info.unknown_chunks_data[2], &info.unknown_chunks_size[2], 3, "uNKc", (unsigned char*)"c00");
1174 
1175   unsigned error = lodepng::encode(png, &image[0], w, h, state);
1176   ASSERT_NO_PNG_ERROR(error);
1177 }
1178 
extractChunkNames(const std::vector<unsigned char> & png)1179 std::string extractChunkNames(const std::vector<unsigned char>& png) {
1180   const unsigned char* chunk = &png[8];
1181   char name[5];
1182   std::string result = "";
1183   for(;;) {
1184     lodepng_chunk_type(name, chunk);
1185     result += (std::string(" ") + name);
1186     if(std::string(name) == "IEND") break;
1187     chunk = lodepng_chunk_next_const(chunk);
1188     assertTrue(chunk < &png.back(), "jumped out of chunks");
1189   }
1190   return result;
1191 }
1192 
testComplexPNG()1193 void testComplexPNG() {
1194   std::cout << "testComplexPNG" << std::endl;
1195 
1196   std::vector<unsigned char> png;
1197   createComplexPNG(png);
1198  {
1199     lodepng::State state;
1200     LodePNGInfo& info = state.info_png;
1201     unsigned w, h;
1202     std::vector<unsigned char> image;
1203     unsigned error = lodepng::decode(image, w, h, state, &png[0], png.size());
1204     ASSERT_NO_PNG_ERROR(error);
1205 
1206     ASSERT_EQUALS(16, w);
1207     ASSERT_EQUALS(17, h);
1208     ASSERT_EQUALS(1, info.background_defined);
1209     ASSERT_EQUALS(127, info.background_r);
1210     ASSERT_EQUALS(1, info.time_defined);
1211     ASSERT_EQUALS(2012, info.time.year);
1212     ASSERT_EQUALS(1, info.time.month);
1213     ASSERT_EQUALS(2, info.time.day);
1214     ASSERT_EQUALS(3, info.time.hour);
1215     ASSERT_EQUALS(4, info.time.minute);
1216     ASSERT_EQUALS(5, info.time.second);
1217     ASSERT_EQUALS(1, info.phys_defined);
1218     ASSERT_EQUALS(1, info.phys_x);
1219     ASSERT_EQUALS(2, info.phys_y);
1220     ASSERT_EQUALS(1, info.phys_unit);
1221 
1222     std::string chunknames = extractChunkNames(png);
1223     //std::string expectednames = " IHDR uNKa uNKa PLTE tRNS bKGD pHYs uNKb IDAT tIME tEXt tEXt tEXt iTXt iTXt uNKc IEND";
1224     std::string expectednames = " IHDR uNKa uNKa PLTE tRNS bKGD pHYs uNKb IDAT tIME zTXt zTXt tEXt iTXt iTXt uNKc IEND";
1225     ASSERT_EQUALS(expectednames, chunknames);
1226 
1227     ASSERT_EQUALS(3, info.text_num);
1228     ASSERT_STRING_EQUALS("key0", info.text_keys[0]);
1229     ASSERT_STRING_EQUALS("string0", info.text_strings[0]);
1230     ASSERT_STRING_EQUALS("key1", info.text_keys[1]);
1231     ASSERT_STRING_EQUALS("string1", info.text_strings[1]);
1232     ASSERT_STRING_EQUALS("LodePNG", info.text_keys[2]);
1233     ASSERT_STRING_EQUALS(LODEPNG_VERSION_STRING, info.text_strings[2]);
1234 
1235     ASSERT_EQUALS(2, info.itext_num);
1236     ASSERT_STRING_EQUALS("ikey0", info.itext_keys[0]);
1237     ASSERT_STRING_EQUALS("ilangtag0", info.itext_langtags[0]);
1238     ASSERT_STRING_EQUALS("itranskey0", info.itext_transkeys[0]);
1239     ASSERT_STRING_EQUALS("istring0", info.itext_strings[0]);
1240     ASSERT_STRING_EQUALS("ikey1", info.itext_keys[1]);
1241     ASSERT_STRING_EQUALS("ilangtag1", info.itext_langtags[1]);
1242     ASSERT_STRING_EQUALS("itranskey1", info.itext_transkeys[1]);
1243     ASSERT_STRING_EQUALS("istring1", info.itext_strings[1]);
1244 
1245     // TODO: test if unknown chunks listed too
1246   }
1247 
1248 
1249   // Test that if read_text_chunks is disabled, we do not get the texts
1250   {
1251     lodepng::State state;
1252     state.decoder.read_text_chunks = 0;
1253     unsigned w, h;
1254     std::vector<unsigned char> image;
1255     unsigned error = lodepng::decode(image, w, h, state, &png[0], png.size());
1256     ASSERT_NO_PNG_ERROR(error);
1257 
1258     ASSERT_EQUALS(0, state.info_png.text_num);
1259     ASSERT_EQUALS(0, state.info_png.itext_num);
1260 
1261     // But we should still get other values.
1262     ASSERT_EQUALS(2012, state.info_png.time.year);
1263   }
1264 }
1265 
1266 // Tests lodepng_inspect_chunk, and also lodepng_chunk_find to find the chunk to inspect
testInspectChunk()1267 void testInspectChunk() {
1268   std::cout << "testInspectChunk" << std::endl;
1269 
1270   std::vector<unsigned char> png;
1271   createComplexPNG(png);
1272 
1273   const unsigned char* chunk;
1274   lodepng::State state;
1275   LodePNGInfo& info = state.info_png;
1276   state.decoder.read_text_chunks = 0;
1277   lodepng_inspect(0, 0, &state, png.data(), png.size());
1278   chunk = lodepng_chunk_find(png.data(), png.data() + png.size(), "tIME");
1279   ASSERT_NOT_EQUALS((const unsigned char*)0, chunk); // should be non-null, since it should find it
1280   ASSERT_EQUALS(0, info.time_defined);
1281   lodepng_inspect_chunk(&state, (size_t)(chunk - png.data()), png.data(), png.size());
1282   ASSERT_EQUALS(1, info.time_defined);
1283   ASSERT_EQUALS(2012, state.info_png.time.year);
1284   ASSERT_EQUALS(1, info.time.month);
1285   ASSERT_EQUALS(2, info.time.day);
1286   ASSERT_EQUALS(3, info.time.hour);
1287   ASSERT_EQUALS(4, info.time.minute);
1288   ASSERT_EQUALS(5, info.time.second);
1289 
1290   ASSERT_EQUALS(0, info.text_num);
1291   chunk = lodepng_chunk_find_const(png.data(), png.data() + png.size(), "zTXt");
1292   lodepng_inspect_chunk(&state, (size_t)(chunk - png.data()), png.data(), png.size());
1293   ASSERT_EQUALS(1, info.text_num);
1294   chunk = lodepng_chunk_find_const(chunk, png.data() + png.size(), "zTXt");
1295   lodepng_inspect_chunk(&state, (size_t)(chunk - png.data()), png.data(), png.size());
1296   ASSERT_EQUALS(2, info.text_num);
1297 }
1298 
1299 //test that, by default, it chooses filter type zero for all scanlines if the image has a palette
testPaletteFilterTypesZero()1300 void testPaletteFilterTypesZero() {
1301   std::cout << "testPaletteFilterTypesZero" << std::endl;
1302 
1303   std::vector<unsigned char> png;
1304   createComplexPNG(png);
1305 
1306   std::vector<unsigned char> filterTypes;
1307   lodepng::getFilterTypes(filterTypes, png);
1308 
1309   ASSERT_EQUALS(17, filterTypes.size());
1310   for(size_t i = 0; i < 17; i++) ASSERT_EQUALS(0, filterTypes[i]);
1311 }
1312 
1313 //tests that there are no crashes with auto color chooser in case of palettes with translucency etc...
testPaletteToPaletteConvert()1314 void testPaletteToPaletteConvert() {
1315   std::cout << "testPaletteToPaletteConvert" << std::endl;
1316   unsigned error;
1317   unsigned w = 16, h = 16;
1318   std::vector<unsigned char> image(w * h);
1319   for(size_t i = 0; i < w * h; i++) image[i] = i % 256;
1320   lodepng::State state;
1321   LodePNGInfo& info = state.info_png;
1322   info.color.colortype = state.info_raw.colortype = LCT_PALETTE;
1323   info.color.bitdepth = state.info_raw.bitdepth = 8;
1324   ASSERT_EQUALS(true, state.encoder.auto_convert);
1325   for(size_t i = 0; i < 256; i++) {
1326     lodepng_palette_add(&info.color, i, i, i, i);
1327   }
1328   std::vector<unsigned char> png;
1329   for(size_t i = 0; i < 256; i++) {
1330     lodepng_palette_add(&state.info_raw, i, i, i, i);
1331   }
1332   error = lodepng::encode(png, &image[0], w, h, state);
1333   ASSERT_NO_PNG_ERROR(error);
1334 }
1335 
1336 //for this test, you have to choose palette colors that cause LodePNG to actually use a palette,
1337 //so don't use all grayscale colors for example
doRGBAToPaletteTest(unsigned char * palette,size_t size,LodePNGColorType expectedType=LCT_PALETTE)1338 void doRGBAToPaletteTest(unsigned char* palette, size_t size, LodePNGColorType expectedType = LCT_PALETTE) {
1339   std::cout << "testRGBToPaletteConvert " << size << std::endl;
1340   unsigned error;
1341   unsigned w = size, h = 257 /*LodePNG encodes no palette if image is too small*/;
1342   std::vector<unsigned char> image(w * h * 4);
1343   for(size_t i = 0; i < image.size(); i++) image[i] = palette[i % (size * 4)];
1344   std::vector<unsigned char> png;
1345   error = lodepng::encode(png, &image[0], w, h);
1346   ASSERT_NO_PNG_ERROR(error);
1347   lodepng::State state;
1348   std::vector<unsigned char> image2;
1349   error = lodepng::decode(image2, w, h, state, png);
1350   ASSERT_NO_PNG_ERROR(error);
1351   ASSERT_EQUALS(image.size(), image2.size());
1352   for(size_t i = 0; i < image.size(); i++) ASSERT_EQUALS(image[i], image2[i]);
1353 
1354   ASSERT_EQUALS(expectedType, state.info_png.color.colortype);
1355   if(expectedType == LCT_PALETTE) {
1356 
1357     ASSERT_EQUALS(size, state.info_png.color.palettesize);
1358     for(size_t i = 0; i < size * 4; i++) ASSERT_EQUALS(state.info_png.color.palette[i], image[i]);
1359   }
1360 }
1361 
testRGBToPaletteConvert()1362 void testRGBToPaletteConvert() {
1363   unsigned char palette1[4] = {1,2,3,4};
1364   doRGBAToPaletteTest(palette1, 1);
1365   unsigned char palette2[8] = {1,2,3,4, 5,6,7,8};
1366   doRGBAToPaletteTest(palette2, 2);
1367   unsigned char palette3[12] = {1,1,1,255, 20,20,20,255, 20,20,21,255};
1368   doRGBAToPaletteTest(palette3, 3);
1369 
1370   std::vector<unsigned char> palette;
1371   for(int i = 0; i < 256; i++) {
1372     palette.push_back(i);
1373     palette.push_back(5);
1374     palette.push_back(6);
1375     palette.push_back(128);
1376   }
1377   doRGBAToPaletteTest(&palette[0], 256);
1378   palette.push_back(5);
1379   palette.push_back(6);
1380   palette.push_back(7);
1381   palette.push_back(8);
1382   doRGBAToPaletteTest(&palette[0], 257, LCT_RGBA);
1383 }
1384 
testColorKeyConvert()1385 void testColorKeyConvert() {
1386   std::cout << "testColorKeyConvert" << std::endl;
1387   unsigned error;
1388   unsigned w = 32, h = 32;
1389   std::vector<unsigned char> image(w * h * 4);
1390   for(size_t i = 0; i < w * h; i++) {
1391     image[i * 4 + 0] = i % 256;
1392     image[i * 4 + 1] = i / 256;
1393     image[i * 4 + 2] = 0;
1394     image[i * 4 + 3] = i == 23 ? 0 : 255;
1395   }
1396   std::vector<unsigned char> png;
1397   error = lodepng::encode(png, &image[0], w, h);
1398   ASSERT_NO_PNG_ERROR(error);
1399 
1400   lodepng::State state;
1401   std::vector<unsigned char> image2;
1402   error = lodepng::decode(image2, w, h, state, png);
1403   ASSERT_NO_PNG_ERROR(error);
1404   ASSERT_EQUALS(32, w);
1405   ASSERT_EQUALS(32, h);
1406   ASSERT_EQUALS(1, state.info_png.color.key_defined);
1407   ASSERT_EQUALS(23, state.info_png.color.key_r);
1408   ASSERT_EQUALS(0, state.info_png.color.key_g);
1409   ASSERT_EQUALS(0, state.info_png.color.key_b);
1410   ASSERT_EQUALS(image.size(), image2.size());
1411   for(size_t i = 0; i < image.size(); i++) {
1412     ASSERT_EQUALS(image[i], image2[i]);
1413   }
1414 }
1415 
testNoAutoConvert()1416 void testNoAutoConvert() {
1417   std::cout << "testNoAutoConvert" << std::endl;
1418   unsigned error;
1419   unsigned w = 32, h = 32;
1420   std::vector<unsigned char> image(w * h * 4);
1421   for(size_t i = 0; i < w * h; i++) {
1422     image[i * 4 + 0] = (i % 2) ? 255 : 0;
1423     image[i * 4 + 1] = (i % 2) ? 255 : 0;
1424     image[i * 4 + 2] = (i % 2) ? 255 : 0;
1425     image[i * 4 + 3] = 0;
1426   }
1427   std::vector<unsigned char> png;
1428   lodepng::State state;
1429   state.info_png.color.colortype = LCT_RGBA;
1430   state.info_png.color.bitdepth = 8;
1431   state.encoder.auto_convert = false;
1432   error = lodepng::encode(png, &image[0], w, h, state);
1433   ASSERT_NO_PNG_ERROR(error);
1434 
1435   lodepng::State state2;
1436   std::vector<unsigned char> image2;
1437   error = lodepng::decode(image2, w, h, state2, png);
1438   ASSERT_NO_PNG_ERROR(error);
1439   ASSERT_EQUALS(32, w);
1440   ASSERT_EQUALS(32, h);
1441   ASSERT_EQUALS(LCT_RGBA, state2.info_png.color.colortype);
1442   ASSERT_EQUALS(8, state2.info_png.color.bitdepth);
1443   ASSERT_EQUALS(image.size(), image2.size());
1444   for(size_t i = 0; i < image.size(); i++) {
1445     ASSERT_EQUALS(image[i], image2[i]);
1446   }
1447 }
1448 
flipBit(unsigned char c,int bitpos)1449 unsigned char flipBit(unsigned char c, int bitpos) {
1450   return c ^ (1 << bitpos);
1451 }
1452 
1453 //Test various broken inputs. Returned errors are not checked, what is tested is
1454 //that is doesn't crash, and, when run with valgrind, no memory warnings are
1455 //given.
testFuzzing()1456 void testFuzzing() {
1457   std::cout << "testFuzzing" << std::endl;
1458   std::vector<unsigned char> png;
1459   createComplexPNG(png);
1460   std::vector<unsigned char> broken = png;
1461   std::vector<unsigned char> result;
1462   std::map<unsigned, unsigned> errors;
1463   unsigned w, h;
1464   lodepng::State state;
1465   state.decoder.ignore_crc = 1;
1466   state.decoder.zlibsettings.ignore_adler32 = 1;
1467   for(size_t i = 0; i < png.size(); i++) {
1468     result.clear();
1469     broken[i] = ~png[i];
1470     errors[lodepng::decode(result, w, h, state, broken)]++;
1471     broken[i] = 0;
1472     errors[lodepng::decode(result, w, h, state, broken)]++;
1473     for(int j = 0; j < 8; j++) {
1474       broken[i] = flipBit(png[i], j);
1475       errors[lodepng::decode(result, w, h, state, broken)]++;
1476     }
1477     broken[i] = 255;
1478     errors[lodepng::decode(result, w, h, state, broken)]++;
1479     broken[i] = png[i]; //fix it again for the next test
1480   }
1481   std::cout << "testFuzzing shrinking" << std::endl;
1482   broken = png;
1483   while(broken.size() > 0) {
1484     broken.resize(broken.size() - 1);
1485     errors[lodepng::decode(result, w, h, state, broken)]++;
1486   }
1487 
1488   //For fun, print the number of each error
1489   std::cout << "Fuzzing error code counts: ";
1490   for(std::map<unsigned, unsigned>::iterator it = errors.begin(); it != errors.end(); ++it) {
1491     std::cout << it->first << ":" << it->second << ", ";
1492   }
1493   std::cout << std::endl;
1494 }
1495 
testCustomZlibCompress()1496 void testCustomZlibCompress() {
1497   std::cout << "testCustomZlibCompress" << std::endl;
1498   Image image;
1499   generateTestImage(image, 5, 5, LCT_RGBA, 8);
1500 
1501   std::vector<unsigned char> encoded;
1502   int customcontext = 5;
1503 
1504   struct TestFun {
1505     static unsigned custom_zlib(unsigned char**, size_t*,
1506                           const unsigned char*, size_t,
1507                           const LodePNGCompressSettings* settings) {
1508       ASSERT_EQUALS(5, *(int*)(settings->custom_context));
1509       return 5555; //return a custom error code to prove this function was called
1510     }
1511   };
1512 
1513   lodepng::State state;
1514   state.encoder.zlibsettings.custom_zlib = TestFun::custom_zlib;
1515   state.encoder.zlibsettings.custom_context = &customcontext;
1516 
1517   unsigned error = lodepng::encode(encoded, image.data, image.width, image.height,
1518                                    state);
1519 
1520   ASSERT_EQUALS(5555, error);
1521 }
1522 
testCustomZlibCompress2()1523 void testCustomZlibCompress2() {
1524   std::cout << "testCustomZlibCompress2" << std::endl;
1525   Image image;
1526   generateTestImage(image, 5, 5, LCT_RGBA, 8);
1527 
1528   std::vector<unsigned char> encoded;
1529 
1530   lodepng::State state;
1531   state.encoder.zlibsettings.custom_zlib = lodepng_zlib_compress;
1532 
1533   unsigned error = lodepng::encode(encoded, image.data, image.width, image.height,
1534                                    state);
1535   ASSERT_NO_PNG_ERROR(error);
1536 
1537   std::vector<unsigned char> decoded;
1538   unsigned w, h;
1539   state.decoder.zlibsettings.ignore_adler32 = 0;
1540   state.decoder.ignore_crc = 0;
1541   error = lodepng::decode(decoded, w, h, state, encoded);
1542   ASSERT_NO_PNG_ERROR(error);
1543   ASSERT_EQUALS(5, w);
1544   ASSERT_EQUALS(5, h);
1545 }
1546 
testCustomDeflate()1547 void testCustomDeflate() {
1548   std::cout << "testCustomDeflate" << std::endl;
1549   Image image;
1550   generateTestImage(image, 5, 5, LCT_RGBA, 8);
1551 
1552   std::vector<unsigned char> encoded;
1553   int customcontext = 5;
1554 
1555   struct TestFun {
1556     static unsigned custom_deflate(unsigned char**, size_t*,
1557                                    const unsigned char*, size_t,
1558                                    const LodePNGCompressSettings* settings) {
1559       ASSERT_EQUALS(5, *(int*)(settings->custom_context));
1560       return 5555; //return a custom error code to prove this function was called
1561     }
1562   };
1563 
1564   lodepng::State state;
1565   state.encoder.zlibsettings.custom_deflate = TestFun::custom_deflate;
1566   state.encoder.zlibsettings.custom_context = &customcontext;
1567 
1568   unsigned error = lodepng::encode(encoded, image.data, image.width, image.height,
1569                                    state);
1570 
1571   ASSERT_EQUALS(5555, error);
1572 }
1573 
testCustomZlibDecompress()1574 void testCustomZlibDecompress() {
1575   std::cout << "testCustomZlibDecompress" << std::endl;
1576   Image image;
1577   generateTestImage(image, 5, 5, LCT_RGBA, 8);
1578 
1579   std::vector<unsigned char> encoded;
1580 
1581   unsigned error_enc = lodepng::encode(encoded, image.data, image.width, image.height,
1582                                    image.colorType, image.bitDepth);
1583   ASSERT_NO_PNG_ERROR_MSG(error_enc, "encoder error not expected");
1584 
1585 
1586   std::vector<unsigned char> decoded;
1587   unsigned w, h;
1588   int customcontext = 5;
1589 
1590   struct TestFun {
1591     static unsigned custom_zlib(unsigned char**, size_t*,
1592                           const unsigned char*, size_t,
1593                           const LodePNGDecompressSettings* settings) {
1594       ASSERT_EQUALS(5, *(int*)(settings->custom_context));
1595       return 5555; //return a custom error code to prove this function was called
1596     }
1597   };
1598 
1599   lodepng::State state;
1600   state.decoder.zlibsettings.custom_zlib = TestFun::custom_zlib;
1601   state.decoder.zlibsettings.custom_context = &customcontext;
1602   state.decoder.zlibsettings.ignore_adler32 = 0;
1603   state.decoder.ignore_crc = 0;
1604   unsigned error = lodepng::decode(decoded, w, h, state, encoded);
1605 
1606   ASSERT_EQUALS(5555, error);
1607 }
1608 
testCustomInflate()1609 void testCustomInflate() {
1610   std::cout << "testCustomInflate" << std::endl;
1611   Image image;
1612   generateTestImage(image, 5, 5, LCT_RGBA, 8);
1613 
1614   std::vector<unsigned char> encoded;
1615 
1616   unsigned error_enc = lodepng::encode(encoded, image.data, image.width, image.height,
1617                                    image.colorType, image.bitDepth);
1618   ASSERT_NO_PNG_ERROR_MSG(error_enc, "encoder error not expected");
1619 
1620 
1621   std::vector<unsigned char> decoded;
1622   unsigned w, h;
1623   int customcontext = 5;
1624 
1625   struct TestFun {
1626     static unsigned custom_inflate(unsigned char**, size_t*,
1627                                    const unsigned char*, size_t,
1628                                    const LodePNGDecompressSettings* settings) {
1629       ASSERT_EQUALS(5, *(int*)(settings->custom_context));
1630       return 5555; //return a custom error code to prove this function was called
1631     }
1632   };
1633 
1634   lodepng::State state;
1635   state.decoder.zlibsettings.custom_inflate = TestFun::custom_inflate;
1636   state.decoder.zlibsettings.custom_context = &customcontext;
1637   state.decoder.zlibsettings.ignore_adler32 = 0;
1638   state.decoder.ignore_crc = 0;
1639   unsigned error = lodepng::decode(decoded, w, h, state, encoded);
1640 
1641   ASSERT_EQUALS(5555, error);
1642 }
1643 
doPngSuiteTinyTest(const std::string & base64,unsigned w,unsigned h,unsigned char r,unsigned char g,unsigned char b,unsigned char a)1644 void doPngSuiteTinyTest(const std::string& base64, unsigned w, unsigned h,
1645                         unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
1646   lodepng::State state;
1647   std::vector<unsigned char> png;
1648   fromBase64(png, base64);
1649   unsigned w2, h2;
1650   std::vector<unsigned char> image;
1651   unsigned error = lodepng::decode(image, w2, h2, state, png);
1652   ASSERT_NO_PNG_ERROR(error);
1653   ASSERT_EQUALS(w, w2);
1654   ASSERT_EQUALS(h, h2);
1655   ASSERT_EQUALS((int)r, (int)image[0]);
1656   ASSERT_EQUALS((int)g, (int)image[1]);
1657   ASSERT_EQUALS((int)b, (int)image[2]);
1658   ASSERT_EQUALS((int)a, (int)image[3]);
1659 
1660   state.encoder.auto_convert = false;
1661   std::vector<unsigned char> png2;
1662   error = lodepng::encode(png2, image, w, h, state);
1663   ASSERT_NO_PNG_ERROR(error);
1664   std::vector<unsigned char> image2;
1665   error = lodepng::decode(image2, w2, h2, state, png2);
1666   ASSERT_NO_PNG_ERROR(error);
1667   for(size_t i = 0; i < image.size(); i++) ASSERT_EQUALS(image[i], image2[i]);
1668 }
1669 
1670 /*checks that both png suite images have the exact same pixel content, e.g. to check that
1671 it decodes an interlaced and non-interlaced corresponding png suite image equally*/
doPngSuiteEqualTest(const std::string & base64a,const std::string & base64b)1672 void doPngSuiteEqualTest(const std::string& base64a, const std::string& base64b) {
1673   lodepng::State state;
1674   std::vector<unsigned char> pnga, pngb;
1675   fromBase64(pnga, base64a);
1676   fromBase64(pngb, base64b);
1677   unsigned wa, ha, wb, hb;
1678   std::vector<unsigned char> imagea, imageb;
1679   ASSERT_NO_PNG_ERROR(lodepng::decode(imagea, wa, ha, state, pnga));
1680   ASSERT_NO_PNG_ERROR(lodepng::decode(imageb, wb, hb, state, pngb));
1681   ASSERT_EQUALS(wa, wb);
1682   ASSERT_EQUALS(ha, hb);
1683 
1684   size_t size = wa * ha * 4;
1685   for(size_t i = 0; i < size; i++) {
1686     if(imagea[i] != imageb[i]) {
1687       std::cout << "x: " << ((i / 4) % wa) << " y: " << ((i / 4) / wa) << " c: " << i % 4 << std::endl;
1688       ASSERT_EQUALS((int)imagea[i], (int)imageb[i]);
1689     }
1690   }
1691 }
1692 
testPngSuiteTiny()1693 void testPngSuiteTiny() {
1694   std::cout << "testPngSuiteTiny" << std::endl;
1695   doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAFS3GZcAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
1696                      "BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=",
1697                      1, 1, 0, 0, 255, 255); //s01n3p01.png
1698   doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
1699                      "BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=",
1700                      1, 1, 0, 0, 255, 255); //s01i3p01.png
1701   doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAC5PL9AAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
1702                      "BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAABpJREFUeJxj+P+H4WoMw605DDfmgEgg"
1703                      "+/8fAHF5CrkeXW0HAAAAAElFTkSuQmCC",
1704                      7, 7, 0, 0, 255, 255); //s07n3p02.png
1705   doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAHOO4/WAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
1706                      "BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAACVJREFUeJxjOMBwgOEBwweGDQyvGf4z"
1707                      "/GFIAcI/DFdjGG7MAZIAweMMgVWC+YkAAAAASUVORK5CYII=",
1708                      7, 7, 0, 0, 255, 255); //s07i3p02.png
1709   doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAgMAAAAOFJJnAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
1710                      "AQEBfC53ggAAAAxQTFRFAP8A/wAA//8AAAD/ZT8rugAAACJJREFUeJxj+B+6igGEGfAw8MnBGKug"
1711                      "LHwMqNL/+BiDzD0AvUl/geqJjhsAAAAASUVORK5CYII=",
1712                      32, 32, 0, 0, 255, 255); //basn3p02.png
1713   doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABGdBTUEAAYagMeiWXwAAAAZQTFRF"
1714                      "7v8iImb/bBrSJgAAABVJREFUeJxj4AcCBjTiAxCgEwOkDgC7Hz/Bk4JmWQAAAABJRU5ErkJggg==",
1715                      32, 32, 238, 255, 34, 255); //basn3p01.png
1716   doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAAGgflrAAAABGdBTUEAAYagMeiWXwAAAF5JREFU"
1717                      "eJzV0jEKwDAMQ1E5W+9/xtygk8AoezLVKgSj2Y8/OICnuFcTE2OgOoJgHQiZAN2C9kDKBOgW3AZC"
1718                      "JkC3oD2QMgG6BbeBkAnQLWgPpExgP28H7E/0GTjPfwAW2EvYX64rn9cAAAAASUVORK5CYII=",
1719                      32, 32, 0, 0, 0, 255); //basn0g16.png
1720   doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAFxhsn9AAAABGdBTUEAAYagMeiWXwAAAOJJREFU"
1721                      "eJy1kTsOwjAQRMdJCqj4XYHD5DAcj1Okyg2okCyBRLOSC0BDERKCI7xJVmgaa/X8PFo7oESJEtka"
1722                      "TeLDjdjjgCMe7eTE96FGd3AL7HvZsdNEaJMVo0GNGm775bgwW6Afj/SAjAY+JsYNXIHtz2xYxTXi"
1723                      "UoOek4AbFcCnDYEK4NMGsgXcMrGHJytkBX5HIP8FAhVANIMVIBVANMPfgUAFEM3wAVyG5cxcecY5"
1724                      "/dup3LVFa1HXmA61LY59f6Ygp1Eg1gZGQaBRILYGdxoFYmtAGgXx9YmCfPD+RMHwuuAFVpjuiRT/"
1725                      "//4AAAAASUVORK5CYII=",
1726                      32, 32, 0, 0, 0, 255); //basi0g16.png
1727 
1728   //s01n3p01.png s01i3p01.png
1729   doPngSuiteEqualTest("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAFS3GZcAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
1730                       "BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=",
1731                       "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
1732                       "BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=");
1733   //s07n3p02.png and s07i3p02.png
1734   doPngSuiteEqualTest("iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAC5PL9AAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
1735                       "BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAABpJREFUeJxj+P+H4WoMw605DDfmgEgg"
1736                       "+/8fAHF5CrkeXW0HAAAAAElFTkSuQmCC",
1737                       "iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAHOO4/WAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
1738                       "BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAACVJREFUeJxjOMBwgOEBwweGDQyvGf4z"
1739                       "/GFIAcI/DFdjGG7MAZIAweMMgVWC+YkAAAAASUVORK5CYII=");
1740   //basn0g16.png and basi0g16.png
1741   doPngSuiteEqualTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAAGgflrAAAABGdBTUEAAYagMeiWXwAAAF5JREFU"
1742                       "eJzV0jEKwDAMQ1E5W+9/xtygk8AoezLVKgSj2Y8/OICnuFcTE2OgOoJgHQiZAN2C9kDKBOgW3AZC"
1743                       "JkC3oD2QMgG6BbeBkAnQLWgPpExgP28H7E/0GTjPfwAW2EvYX64rn9cAAAAASUVORK5CYII=",
1744                       "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAFxhsn9AAAABGdBTUEAAYagMeiWXwAAAOJJREFU"
1745                       "eJy1kTsOwjAQRMdJCqj4XYHD5DAcj1Okyg2okCyBRLOSC0BDERKCI7xJVmgaa/X8PFo7oESJEtka"
1746                       "TeLDjdjjgCMe7eTE96FGd3AL7HvZsdNEaJMVo0GNGm775bgwW6Afj/SAjAY+JsYNXIHtz2xYxTXi"
1747                       "UoOek4AbFcCnDYEK4NMGsgXcMrGHJytkBX5HIP8FAhVANIMVIBVANMPfgUAFEM3wAVyG5cxcecY5"
1748                       "/dup3LVFa1HXmA61LY59f6Ygp1Eg1gZGQaBRILYGdxoFYmtAGgXx9YmCfPD+RMHwuuAFVpjuiRT/"
1749                       "//4AAAAASUVORK5CYII=");
1750 }
1751 
testChunkUtil()1752 void testChunkUtil() {
1753   std::cout << "testChunkUtil" << std::endl;
1754   std::vector<unsigned char> png;
1755   createComplexPNG(png);
1756 
1757   std::vector<std::string> names[3];
1758   std::vector<std::vector<unsigned char> > chunks[3];
1759 
1760   assertNoError(lodepng::getChunks(names, chunks, png));
1761 
1762   std::vector<std::vector<unsigned char> > chunks2[3];
1763   chunks2[0].push_back(chunks[2][2]); //zTXt
1764   chunks2[1].push_back(chunks[2][3]); //tEXt
1765   chunks2[2].push_back(chunks[2][4]); //iTXt
1766 
1767   assertNoError(lodepng::insertChunks(png, chunks2));
1768 
1769   std::string chunknames = extractChunkNames(png);
1770   //                                        chunks2[0]                    chunks2[1]                                   chunks2[2]
1771   //                                             v                             v                                            v
1772   std::string expectednames = " IHDR uNKa uNKa zTXt PLTE tRNS bKGD pHYs uNKb tEXt IDAT tIME zTXt zTXt tEXt iTXt iTXt uNKc iTXt IEND";
1773   ASSERT_EQUALS(expectednames, chunknames);
1774 
1775   std::vector<unsigned char> image;
1776   unsigned w, h;
1777   ASSERT_NO_PNG_ERROR(lodepng::decode(image, w, h, png));
1778 }
1779 
1780 //Test that when decoding to 16-bit per channel, it always uses big endian consistently.
1781 //It should always output big endian, the convention used inside of PNG, even though x86 CPU's are little endian.
test16bitColorEndianness()1782 void test16bitColorEndianness() {
1783   std::cout << "test16bitColorEndianness" << std::endl;
1784 
1785   //basn0g16.png from the PNG test suite
1786   std::string base64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAAGgflrAAAABGdBTUEAAYagMeiWXwAAAF5JREFU"
1787                        "eJzV0jEKwDAMQ1E5W+9/xtygk8AoezLVKgSj2Y8/OICnuFcTE2OgOoJgHQiZAN2C9kDKBOgW3AZC"
1788                        "JkC3oD2QMgG6BbeBkAnQLWgPpExgP28H7E/0GTjPfwAW2EvYX64rn9cAAAAASUVORK5CYII=";
1789   std::vector<unsigned char> png;
1790   fromBase64(png, base64);
1791   unsigned w, h;
1792   std::vector<unsigned char> image;
1793   lodepng::State state;
1794 
1795   // Decode from 16-bit gray image to 16-bit per channel RGBA
1796   state.info_raw.bitdepth = 16;
1797   ASSERT_NO_PNG_ERROR(lodepng::decode(image, w, h, state, png));
1798   ASSERT_EQUALS(0x09, image[8]);
1799   ASSERT_EQUALS(0x00, image[9]);
1800 
1801   // Decode from 16-bit gray image to 16-bit gray raw image (no conversion)
1802   image.clear();
1803   state = lodepng::State();
1804   state.decoder.color_convert = false;
1805   ASSERT_NO_PNG_ERROR(lodepng::decode(image, w, h, state, png));
1806   ASSERT_EQUALS(0x09, image[2]);
1807   ASSERT_EQUALS(0x00, image[3]);
1808 
1809   // Decode from 16-bit per channel RGB image to 16-bit per channel RGBA
1810   base64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAIAAACsiDHgAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
1811            "DQ0N0DeNwQAAAH5JREFUeJztl8ENxEAIAwcJ6cpI+q8qKeNepAgelq2dCjz4AdQM1jRcf3WIDQ13"
1812            "qUNsiBBQZ1gR0cARUFIz3pug3586wo5+rOcfIaBOsCSggSOgpcB8D4D3R9DgfUyECIhDbAhp4Ajo"
1813            "KPD+CBq8P4IG72MiQkCdYUVEA0dAyQcwUyZpXH92ZwAAAABJRU5ErkJggg=="; //cs3n2c16.png
1814   png.clear();
1815   fromBase64(png, base64);
1816   image.clear();
1817   state = lodepng::State();
1818   state.info_raw.bitdepth = 16;
1819   ASSERT_NO_PNG_ERROR(lodepng::decode(image, w, h, state, png));
1820   ASSERT_EQUALS(0x1f, image[258]);
1821   ASSERT_EQUALS(0xf9, image[259]);
1822 
1823   // Decode from 16-bit per channel RGB image to 16-bit per channel RGBA raw image (no conversion)
1824   image.clear();
1825   state = lodepng::State();
1826   state.decoder.color_convert = false;
1827   ASSERT_NO_PNG_ERROR(lodepng::decode(image, w, h, state, png));
1828 
1829   ASSERT_EQUALS(0x1f, image[194]);
1830   ASSERT_EQUALS(0xf9, image[195]);
1831 
1832   image.clear();
1833   state = lodepng::State();
1834 
1835   // Decode from palette image to 16-bit per channel RGBA
1836   base64 = "iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAC5PL9AAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
1837            "BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAABpJREFUeJxj+P+H4WoMw605DDfmgEgg"
1838            "+/8fAHF5CrkeXW0HAAAAAElFTkSuQmCC"; //s07n3p02.png
1839   png.clear();
1840   fromBase64(png, base64);
1841   image.clear();
1842   state = lodepng::State();
1843   state.info_raw.bitdepth = 16;
1844   ASSERT_NO_PNG_ERROR(lodepng::decode(image, w, h, state, png));
1845   ASSERT_EQUALS(0x77, image[84]);
1846   ASSERT_EQUALS(0x77, image[85]);
1847 }
1848 
testPredefinedFilters()1849 void testPredefinedFilters() {
1850   size_t w = 32, h = 32;
1851   std::cout << "testPredefinedFilters" << std::endl;
1852   Image image;
1853   generateTestImage(image, w, h, LCT_RGBA, 8);
1854 
1855   // everything to filter type '3'
1856   std::vector<unsigned char> predefined(h, 3);
1857   lodepng::State state;
1858   state.encoder.filter_strategy = LFS_PREDEFINED;
1859   state.encoder.filter_palette_zero = 0;
1860   state.encoder.predefined_filters = &predefined[0];
1861 
1862   std::vector<unsigned char> png;
1863   unsigned error = lodepng::encode(png, &image.data[0], w, h, state);
1864   assertNoError(error);
1865 
1866   std::vector<unsigned char> outfilters;
1867   error = lodepng::getFilterTypes(outfilters, png);
1868   assertNoError(error);
1869 
1870   ASSERT_EQUALS(outfilters.size(), h);
1871   for(size_t i = 0; i < h; i++) ASSERT_EQUALS(3, outfilters[i]);
1872 }
1873 
testEncoderErrors()1874 void testEncoderErrors() {
1875   std::cout << "testEncoderErrors" << std::endl;
1876 
1877   std::vector<unsigned char> png;
1878   unsigned w = 32, h = 32;
1879   Image image;
1880   generateTestImage(image, w, h);
1881 
1882   lodepng::State def;
1883 
1884   lodepng::State state;
1885 
1886   ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state));
1887 
1888   // test window sizes
1889   state.encoder.zlibsettings.windowsize = 0;
1890   ASSERT_EQUALS(60, lodepng::encode(png, &image.data[0], w, h, state));
1891   state.encoder.zlibsettings.windowsize = 65536;
1892   ASSERT_EQUALS(60, lodepng::encode(png, &image.data[0], w, h, state));
1893   state.encoder.zlibsettings.windowsize = 1000; // not power of two
1894   ASSERT_EQUALS(90, lodepng::encode(png, &image.data[0], w, h, state));
1895   state.encoder.zlibsettings.windowsize = 256;
1896   ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state));
1897 
1898   state = def;
1899   state.info_png.color.bitdepth = 3;
1900   ASSERT_EQUALS(37, lodepng::encode(png, &image.data[0], w, h, state));
1901 
1902   state = def;
1903   state.info_png.color.colortype = (LodePNGColorType)5;
1904   ASSERT_EQUALS(31, lodepng::encode(png, &image.data[0], w, h, state));
1905 
1906   state = def;
1907   state.info_png.color.colortype = LCT_PALETTE;
1908   ASSERT_EQUALS(68, lodepng::encode(png, &image.data[0], w, h, state));
1909 
1910   state = def;
1911   state.info_png.interlace_method = 0;
1912   ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state));
1913   state.info_png.interlace_method = 1;
1914   ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state));
1915   state.info_png.interlace_method = 2;
1916   ASSERT_EQUALS(71, lodepng::encode(png, &image.data[0], w, h, state));
1917 
1918   state = def;
1919   state.encoder.zlibsettings.btype = 0;
1920   ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state));
1921   state.encoder.zlibsettings.btype = 1;
1922   ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state));
1923   state.encoder.zlibsettings.btype = 2;
1924   ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state));
1925   state.encoder.zlibsettings.btype = 3;
1926   ASSERT_EQUALS(61, lodepng::encode(png, &image.data[0], w, h, state));
1927 }
1928 
addColor(std::vector<unsigned char> & colors,unsigned char r,unsigned char g,unsigned char b,unsigned char a)1929 void addColor(std::vector<unsigned char>& colors, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
1930   colors.push_back(r);
1931   colors.push_back(g);
1932   colors.push_back(b);
1933   colors.push_back(a);
1934 }
1935 
addColor16(std::vector<unsigned char> & colors,unsigned short r,unsigned short g,unsigned short b,unsigned short a)1936 void addColor16(std::vector<unsigned char>& colors, unsigned short r, unsigned short g, unsigned short b, unsigned short a) {
1937   colors.push_back(r & 255);
1938   colors.push_back((r >> 8) & 255);
1939   colors.push_back(g & 255);
1940   colors.push_back((g >> 8) & 255);
1941   colors.push_back(b & 255);
1942   colors.push_back((b >> 8) & 255);
1943   colors.push_back(a & 255);
1944   colors.push_back((a >> 8) & 255);
1945 }
1946 
1947 // Tests auto_convert
1948 // colors is in RGBA, inbitdepth must be 8 or 16, the amount of bits per channel.
1949 // colortype and bitdepth are the expected values. insize is amount of pixels. So the amount of bytes is insize * 4 * (inbitdepth / 8)
testAutoColorModel(const std::vector<unsigned char> & colors,unsigned inbitdepth,LodePNGColorType colortype,unsigned bitdepth,bool key)1950 void testAutoColorModel(const std::vector<unsigned char>& colors, unsigned inbitdepth, LodePNGColorType colortype, unsigned bitdepth, bool key) {
1951   std::cout << "testAutoColorModel " << inbitdepth << " " << colortype << " " << bitdepth << " " << key << std::endl;
1952   size_t innum = colors.size() / 4 * inbitdepth / 8;
1953   size_t num = innum < 65536 ? 65536 : innum; // Make image bigger so the convert doesn't avoid palette due to small image.
1954   std::vector<unsigned char> colors2(num * 4 * (inbitdepth / 8));
1955   for(size_t i = 0; i < colors2.size(); i++) colors2[i] = colors[i % colors.size()];
1956 
1957   std::vector<unsigned char> png;
1958   lodepng::encode(png, colors2, num, 1, LCT_RGBA, inbitdepth);
1959 
1960   // now extract the color type it chose
1961   unsigned w, h;
1962   lodepng::State state;
1963   std::vector<unsigned char> decoded;
1964   lodepng::decode(decoded, w, h, state, png);
1965   ASSERT_EQUALS(num, w);
1966   ASSERT_EQUALS(1, h);
1967   ASSERT_EQUALS(colortype, state.info_png.color.colortype);
1968   ASSERT_EQUALS(bitdepth, state.info_png.color.bitdepth);
1969   ASSERT_EQUALS(key, state.info_png.color.key_defined);
1970   // also check that the PNG decoded correctly and has same colors as input
1971   if(inbitdepth == 8) { for(size_t i = 0; i < colors.size(); i++) ASSERT_EQUALS(colors[i], decoded[i]); }
1972   else { for(size_t i = 0; i < colors.size() / 2; i++) ASSERT_EQUALS(colors[i * 2], decoded[i]); }
1973 }
1974 
testAutoColorModels()1975 void testAutoColorModels() {
1976   // 1-bit gray
1977   std::vector<unsigned char> gray1;
1978   for(size_t i = 0; i < 2; i++) addColor(gray1, i * 255, i * 255, i * 255, 255);
1979   testAutoColorModel(gray1, 8, LCT_GREY, 1, false);
1980 
1981   // 2-bit gray
1982   std::vector<unsigned char> gray2;
1983   for(size_t i = 0; i < 4; i++) addColor(gray2, i * 85, i * 85, i * 85, 255);
1984   testAutoColorModel(gray2, 8, LCT_GREY, 2, false);
1985 
1986   // 4-bit gray
1987   std::vector<unsigned char> gray4;
1988   for(size_t i = 0; i < 16; i++) addColor(gray4, i * 17, i * 17, i * 17, 255);
1989   testAutoColorModel(gray4, 8, LCT_GREY, 4, false);
1990 
1991   // 8-bit gray
1992   std::vector<unsigned char> gray8;
1993   for(size_t i = 0; i < 256; i++) addColor(gray8, i, i, i, 255);
1994   testAutoColorModel(gray8, 8, LCT_GREY, 8, false);
1995 
1996   // 16-bit gray
1997   std::vector<unsigned char> gray16;
1998   for(size_t i = 0; i < 257; i++) addColor16(gray16, i, i, i, 65535);
1999   testAutoColorModel(gray16, 16, LCT_GREY, 16, false);
2000 
2001   // 8-bit gray+alpha
2002   std::vector<unsigned char> gray8a;
2003   for(size_t i = 0; i < 17; i++) addColor(gray8a, i, i, i, i);
2004   testAutoColorModel(gray8a, 8, LCT_GREY_ALPHA, 8, false);
2005 
2006   // 16-bit gray+alpha
2007   std::vector<unsigned char> gray16a;
2008   for(size_t i = 0; i < 257; i++) addColor16(gray16a, i, i, i, i);
2009   testAutoColorModel(gray16a, 16, LCT_GREY_ALPHA, 16, false);
2010 
2011 
2012   // various palette tests
2013   std::vector<unsigned char> palette;
2014   addColor(palette, 0, 0, 1, 255);
2015   testAutoColorModel(palette, 8, LCT_PALETTE, 1, false);
2016   addColor(palette, 0, 0, 2, 255);
2017   testAutoColorModel(palette, 8, LCT_PALETTE, 1, false);
2018   for(int i = 3; i <= 4; i++) addColor(palette, 0, 0, i, 255);
2019   testAutoColorModel(palette, 8, LCT_PALETTE, 2, false);
2020   for(int i = 5; i <= 7; i++) addColor(palette, 0, 0, i, 255);
2021   testAutoColorModel(palette, 8, LCT_PALETTE, 4, false);
2022   for(int i = 8; i <= 17; i++) addColor(palette, 0, 0, i, 255);
2023   testAutoColorModel(palette, 8, LCT_PALETTE, 8, false);
2024   addColor(palette, 0, 0, 18, 0); // transparent
2025   testAutoColorModel(palette, 8, LCT_PALETTE, 8, false);
2026   addColor(palette, 0, 0, 18, 1); // translucent
2027   testAutoColorModel(palette, 8, LCT_PALETTE, 8, false);
2028 
2029   // 1-bit gray + alpha not possible, becomes palette
2030   std::vector<unsigned char> gray1a;
2031   for(size_t i = 0; i < 2; i++) addColor(gray1a, i, i, i, 128);
2032   testAutoColorModel(gray1a, 8, LCT_PALETTE, 1, false);
2033 
2034   // 2-bit gray + alpha not possible, becomes palette
2035   std::vector<unsigned char> gray2a;
2036   for(size_t i = 0; i < 4; i++) addColor(gray2a, i, i, i, 128);
2037   testAutoColorModel(gray2a, 8, LCT_PALETTE, 2, false);
2038 
2039   // 4-bit gray + alpha not possible, becomes palette
2040   std::vector<unsigned char> gray4a;
2041   for(size_t i = 0; i < 16; i++) addColor(gray4a, i, i, i, 128);
2042   testAutoColorModel(gray4a, 8, LCT_PALETTE, 4, false);
2043 
2044   // 8-bit rgb
2045   std::vector<unsigned char> rgb = gray8;
2046   addColor(rgb, 255, 0, 0, 255);
2047   testAutoColorModel(rgb, 8, LCT_RGB, 8, false);
2048 
2049   // 8-bit rgb + key
2050   std::vector<unsigned char> rgb_key = rgb;
2051   addColor(rgb_key, 128, 0, 0, 0);
2052   testAutoColorModel(rgb_key, 8, LCT_RGB, 8, true);
2053 
2054   // 8-bit rgb, not key due to edge case: single key color, but opaque color has same RGB value
2055   std::vector<unsigned char> rgb_key2 = rgb_key;
2056   addColor(rgb_key2, 128, 0, 0, 255); // same color but opaque ==> no more key
2057   testAutoColorModel(rgb_key2, 8, LCT_RGBA, 8, false);
2058 
2059   // 8-bit rgb, not key due to semi translucent
2060   std::vector<unsigned char> rgb_key3 = rgb_key;
2061   addColor(rgb_key3, 128, 0, 0, 255); // semi-translucent ==> no more key
2062   testAutoColorModel(rgb_key3, 8, LCT_RGBA, 8, false);
2063 
2064   // 8-bit rgb, not key due to multiple transparent colors
2065   std::vector<unsigned char> rgb_key4 = rgb_key;
2066   addColor(rgb_key4, 128, 0, 0, 255);
2067   addColor(rgb_key4, 129, 0, 0, 255); // two different transparent colors ==> no more key
2068   testAutoColorModel(rgb_key4, 8, LCT_RGBA, 8, false);
2069 
2070   // 1-bit gray with key
2071   std::vector<unsigned char> gray1_key = gray1;
2072   gray1_key[7] = 0;
2073   testAutoColorModel(gray1_key, 8, LCT_GREY, 1, true);
2074 
2075   // 2-bit gray with key
2076   std::vector<unsigned char> gray2_key = gray2;
2077   gray2_key[7] = 0;
2078   testAutoColorModel(gray2_key, 8, LCT_GREY, 2, true);
2079 
2080   // 4-bit gray with key
2081   std::vector<unsigned char> gray4_key = gray4;
2082   gray4_key[7] = 0;
2083   testAutoColorModel(gray4_key, 8, LCT_GREY, 4, true);
2084 
2085   // 8-bit gray with key
2086   std::vector<unsigned char> gray8_key = gray8;
2087   gray8_key[7] = 0;
2088   testAutoColorModel(gray8_key, 8, LCT_GREY, 8, true);
2089 
2090   // 16-bit gray with key
2091   std::vector<unsigned char> gray16_key = gray16;
2092   gray16_key[14] = gray16_key[15] = 0;
2093   testAutoColorModel(gray16_key, 16, LCT_GREY, 16, true);
2094 
2095   // a single 16-bit color, can't become palette due to being 16-bit
2096   std::vector<unsigned char> small16;
2097   addColor16(small16, 1, 0, 0, 65535);
2098   testAutoColorModel(small16, 16, LCT_RGB, 16, false);
2099 
2100   std::vector<unsigned char> small16a;
2101   addColor16(small16a, 1, 0, 0, 1);
2102   testAutoColorModel(small16a, 16, LCT_RGBA, 16, false);
2103 
2104   // what we provide as 16-bit is actually representable as 8-bit, so 8-bit palette expected for single color
2105   std::vector<unsigned char> not16;
2106   addColor16(not16, 257, 257, 257, 0);
2107   testAutoColorModel(not16, 16, LCT_PALETTE, 1, false);
2108 
2109   // the rgb color is representable as 8-bit, but the alpha channel only as 16-bit, so ensure it uses 16-bit and not palette for this single color
2110   std::vector<unsigned char> alpha16;
2111   addColor16(alpha16, 257, 0, 0, 10000);
2112   testAutoColorModel(alpha16, 16, LCT_RGBA, 16, false);
2113 
2114   // 1-bit gray, with attempt to get color key but can't do it due to opaque color with same value
2115   std::vector<unsigned char> gray1k;
2116   addColor(gray1k, 0, 0, 0, 255);
2117   addColor(gray1k, 255, 255, 255, 255);
2118   addColor(gray1k, 255, 255, 255, 0);
2119   testAutoColorModel(gray1k, 8, LCT_PALETTE, 2, false);
2120 }
2121 
testPaletteToPaletteDecode()2122 void testPaletteToPaletteDecode() {
2123   std::cout << "testPaletteToPaletteDecode" << std::endl;
2124   // It's a bit big for a 2x2 image... but this tests needs one with 256 palette entries in it.
2125   std::string base64 = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAMAAABFaP0WAAAAA3NCSVQICAjb4U/gAAADAFBMVEUA"
2126                        "AAAAADMAAGYAAJkAAMwAAP8AMwAAMzMAM2YAM5kAM8wAM/8AZgAAZjMAZmYAZpkAZswAZv8AmQAA"
2127                        "mTMAmWYAmZkAmcwAmf8AzAAAzDMAzGYAzJkAzMwAzP8A/wAA/zMA/2YA/5kA/8wA//8zAAAzADMz"
2128                        "AGYzAJkzAMwzAP8zMwAzMzMzM2YzM5kzM8wzM/8zZgAzZjMzZmYzZpkzZswzZv8zmQAzmTMzmWYz"
2129                        "mZkzmcwzmf8zzAAzzDMzzGYzzJkzzMwzzP8z/wAz/zMz/2Yz/5kz/8wz//9mAABmADNmAGZmAJlm"
2130                        "AMxmAP9mMwBmMzNmM2ZmM5lmM8xmM/9mZgBmZjNmZmZmZplmZsxmZv9mmQBmmTNmmWZmmZlmmcxm"
2131                        "mf9mzABmzDNmzGZmzJlmzMxmzP9m/wBm/zNm/2Zm/5lm/8xm//+ZAACZADOZAGaZAJmZAMyZAP+Z"
2132                        "MwCZMzOZM2aZM5mZM8yZM/+ZZgCZZjOZZmaZZpmZZsyZZv+ZmQCZmTOZmWaZmZmZmcyZmf+ZzACZ"
2133                        "zDOZzGaZzJmZzMyZzP+Z/wCZ/zOZ/2aZ/5mZ/8yZ///MAADMADPMAGbMAJnMAMzMAP/MMwDMMzPM"
2134                        "M2bMM5nMM8zMM//MZgDMZjPMZmbMZpnMZszMZv/MmQDMmTPMmWbMmZnMmczMmf/MzADMzDPMzGbM"
2135                        "zJnMzMzMzP/M/wDM/zPM/2bM/5nM/8zM////AAD/ADP/AGb/AJn/AMz/AP//MwD/MzP/M2b/M5n/"
2136                        "M8z/M///ZgD/ZjP/Zmb/Zpn/Zsz/Zv//mQD/mTP/mWb/mZn/mcz/mf//zAD/zDP/zGb/zJn/zMz/"
2137                        "zP///wD//zP//2b//5n//8z///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2138                        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2139                        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABlenwdAAABAHRSTlP/////////////////////////"
2140                        "////////////////////////////////////////////////////////////////////////////"
2141                        "////////////////////////////////////////////////////////////////////////////"
2142                        "////////////////////////////////////////////////////////////////////////////"
2143                        "//////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2144                        "AAAAAAAAAAAAG8mZagAAAAlwSFlzAAAOTQAADpwB3vacVwAAAA5JREFUCJlj2CLHwHodAATjAa+k"
2145                        "lTE5AAAAAElFTkSuQmCC";
2146   std::vector<unsigned char> png;
2147   fromBase64(png, base64);
2148 
2149   std::vector<unsigned char> image;
2150   unsigned width, height;
2151   unsigned error = lodepng::decode(image, width, height, png, LCT_PALETTE, 8);
2152   ASSERT_EQUALS(0, error);
2153   ASSERT_EQUALS(2, width);
2154   ASSERT_EQUALS(2, height);
2155   ASSERT_EQUALS(180, image[0]);
2156   ASSERT_EQUALS(30, image[1]);
2157   ASSERT_EQUALS(5, image[2]);
2158   ASSERT_EQUALS(215, image[3]);
2159 }
2160 
2161 //2-bit palette
testPaletteToPaletteDecode2()2162 void testPaletteToPaletteDecode2() {
2163   std::cout << "testPaletteToPaletteDecode2" << std::endl;
2164   std::string base64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAgMAAAAOFJJnAAAADFBMVEX/AAAA/wAAAP/////7AGD2AAAAE0lEQVR4AWMQhAKG3VCALDIqAgDl2WYBCQHY9gAAAABJRU5ErkJggg==";
2165   std::vector<unsigned char> png;
2166   fromBase64(png, base64);
2167 
2168   std::vector<unsigned char> image;
2169   unsigned width, height;
2170   unsigned error = lodepng::decode(image, width, height, png, LCT_PALETTE, 8);
2171   ASSERT_EQUALS(0, error);
2172   ASSERT_EQUALS(32, width);
2173   ASSERT_EQUALS(32, height);
2174   ASSERT_EQUALS(0, image[0]);
2175   ASSERT_EQUALS(1, image[1]);
2176 
2177   //Now add a user-specified output palette, that differs from the input palette. That should give error 82.
2178   LodePNGState state;
2179   lodepng_state_init(&state);
2180   state.info_raw.colortype = LCT_PALETTE;
2181   state.info_raw.bitdepth = 8;
2182   lodepng_palette_add(&state.info_raw, 0, 0, 0, 255);
2183   lodepng_palette_add(&state.info_raw, 1, 1, 1, 255);
2184   lodepng_palette_add(&state.info_raw, 2, 2, 2, 255);
2185   lodepng_palette_add(&state.info_raw, 3, 3, 3, 255);
2186   unsigned char* image2 = 0;
2187   unsigned error2 = lodepng_decode(&image2, &width, &height, &state, &png[0], png.size());
2188   lodepng_state_cleanup(&state);
2189   ASSERT_EQUALS(82, error2);
2190   free(image2);
2191 }
2192 
assertColorProfileDataEqual(const lodepng::State & a,const lodepng::State & b)2193 void assertColorProfileDataEqual(const lodepng::State& a, const lodepng::State& b) {
2194   ASSERT_EQUALS(a.info_png.gama_defined, b.info_png.gama_defined);
2195   if(a.info_png.gama_defined) {
2196     ASSERT_EQUALS(a.info_png.gama_gamma, b.info_png.gama_gamma);
2197   }
2198 
2199   ASSERT_EQUALS(a.info_png.chrm_defined, b.info_png.chrm_defined);
2200   if(a.info_png.chrm_defined) {
2201     ASSERT_EQUALS(a.info_png.chrm_white_x, b.info_png.chrm_white_x);
2202     ASSERT_EQUALS(a.info_png.chrm_white_y, b.info_png.chrm_white_y);
2203     ASSERT_EQUALS(a.info_png.chrm_red_x, b.info_png.chrm_red_x);
2204     ASSERT_EQUALS(a.info_png.chrm_red_y, b.info_png.chrm_red_y);
2205     ASSERT_EQUALS(a.info_png.chrm_green_x, b.info_png.chrm_green_x);
2206     ASSERT_EQUALS(a.info_png.chrm_green_y, b.info_png.chrm_green_y);
2207     ASSERT_EQUALS(a.info_png.chrm_blue_x, b.info_png.chrm_blue_x);
2208     ASSERT_EQUALS(a.info_png.chrm_blue_y, b.info_png.chrm_blue_y);
2209   }
2210 
2211   ASSERT_EQUALS(a.info_png.srgb_defined, b.info_png.srgb_defined);
2212   if(a.info_png.srgb_defined) {
2213     ASSERT_EQUALS(a.info_png.srgb_intent, b.info_png.srgb_intent);
2214   }
2215 
2216   ASSERT_EQUALS(a.info_png.iccp_defined, b.info_png.iccp_defined);
2217   if(a.info_png.iccp_defined) {
2218     //ASSERT_EQUALS(std::string(a.info_png.iccp_name), std::string(b.info_png.iccp_name));
2219     ASSERT_EQUALS(a.info_png.iccp_profile_size, b.info_png.iccp_profile_size);
2220     for(size_t i = 0; i < a.info_png.iccp_profile_size; ++i) {
2221       ASSERT_EQUALS(a.info_png.iccp_profile[i], b.info_png.iccp_profile[i]);
2222     }
2223   }
2224 }
2225 
2226 // Tests the gAMA, cHRM, sRGB, iCCP chunks
testColorProfile()2227 void testColorProfile() {
2228   std::cout << "testColorProfile" << std::endl;
2229   {
2230     unsigned error;
2231     unsigned w = 32, h = 32;
2232     std::vector<unsigned char> image(w * h * 4);
2233     for(size_t i = 0; i < image.size(); i++) image[i] = i & 255;
2234     std::vector<unsigned char> png;
2235     lodepng::State state;
2236     state.info_png.gama_defined = 1;
2237     state.info_png.gama_gamma = 12345;
2238     state.info_png.chrm_defined = 1;
2239     state.info_png.chrm_white_x = 10;
2240     state.info_png.chrm_white_y = 20;
2241     state.info_png.chrm_red_x = 30;
2242     state.info_png.chrm_red_y = 40;
2243     state.info_png.chrm_green_x = 100000;
2244     state.info_png.chrm_green_y = 200000;
2245     state.info_png.chrm_blue_x = 300000;
2246     state.info_png.chrm_blue_y = 400000;
2247     error = lodepng::encode(png, &image[0], w, h, state);
2248     ASSERT_NO_PNG_ERROR(error);
2249 
2250     lodepng::State state2;
2251     std::vector<unsigned char> image2;
2252     error = lodepng::decode(image2, w, h, state2, png);
2253     ASSERT_NO_PNG_ERROR(error);
2254     assertColorProfileDataEqual(state, state2);
2255     ASSERT_EQUALS(32, w);
2256     ASSERT_EQUALS(32, h);
2257     ASSERT_EQUALS(image.size(), image2.size());
2258     for(size_t i = 0; i < image.size(); i++) ASSERT_EQUALS(image[i], image2[i]);
2259   }
2260   {
2261     unsigned error;
2262     unsigned w = 32, h = 32;
2263     std::vector<unsigned char> image(w * h * 4);
2264     for(size_t i = 0; i < image.size(); i++) image[i] = i & 255;
2265     std::vector<unsigned char> png;
2266     lodepng::State state;
2267     state.info_png.srgb_defined = 1;
2268     state.info_png.srgb_intent = 2;
2269     error = lodepng::encode(png, &image[0], w, h, state);
2270     ASSERT_NO_PNG_ERROR(error);
2271 
2272     lodepng::State state2;
2273     std::vector<unsigned char> image2;
2274     error = lodepng::decode(image2, w, h, state2, png);
2275     ASSERT_NO_PNG_ERROR(error);
2276     assertColorProfileDataEqual(state, state2);
2277     ASSERT_EQUALS(32, w);
2278     ASSERT_EQUALS(32, h);
2279     ASSERT_EQUALS(image.size(), image2.size());
2280     for(size_t i = 0; i < image.size(); i++) ASSERT_EQUALS(image[i], image2[i]);
2281   }
2282   {
2283     unsigned error;
2284     unsigned w = 32, h = 32;
2285     std::vector<unsigned char> image(w * h * 4);
2286     for(size_t i = 0; i < image.size(); i++) image[i] = i & 255;
2287     std::vector<unsigned char> png;
2288     lodepng::State state;
2289     state.info_png.iccp_defined = 1;
2290     std::string testprofile = "0123456789abcdefRGB fake iccp profile for testing";
2291     testprofile[0] = testprofile[1] = 0;
2292     lodepng_set_icc(&state.info_png, "test", (const unsigned char*)testprofile.c_str(), testprofile.size());
2293     error = lodepng::encode(png, &image[0], w, h, state);
2294     ASSERT_NO_PNG_ERROR(error);
2295 
2296     lodepng::State state2;
2297     std::vector<unsigned char> image2;
2298     error = lodepng::decode(image2, w, h, state2, png);
2299     ASSERT_NO_PNG_ERROR(error);
2300     assertColorProfileDataEqual(state, state2);
2301     ASSERT_EQUALS(32, w);
2302     ASSERT_EQUALS(32, h);
2303     ASSERT_EQUALS(image.size(), image2.size());
2304     for(size_t i = 0; i < image.size(); i++) ASSERT_EQUALS(image[i], image2[i]);
2305   }
2306 
2307   // grayscale ICC profile
2308   {
2309     unsigned error;
2310     unsigned w = 32, h = 32;
2311     std::vector<unsigned char> image(w * h * 4);
2312     for(size_t i = 0; i + 4 <= image.size(); i += 4) {
2313       image[i] = image[i + 1] = image[i + 2] = image[i + 3] = i;
2314     }
2315     std::vector<unsigned char> png;
2316     lodepng::State state;
2317     state.info_png.iccp_defined = 1;
2318     std::string testprofile = "0123456789abcdefGRAYfake iccp profile for testing";
2319     testprofile[0] = testprofile[1] = 0;
2320     lodepng_set_icc(&state.info_png, "test", (const unsigned char*)testprofile.c_str(), testprofile.size());
2321     error = lodepng::encode(png, &image[0], w, h, state);
2322     ASSERT_NO_PNG_ERROR(error);
2323 
2324     lodepng::State state2;
2325     std::vector<unsigned char> image2;
2326     error = lodepng::decode(image2, w, h, state2, png);
2327     ASSERT_NO_PNG_ERROR(error);
2328     assertColorProfileDataEqual(state, state2);
2329     ASSERT_EQUALS(32, w);
2330     ASSERT_EQUALS(32, h);
2331     ASSERT_EQUALS(image.size(), image2.size());
2332     for(size_t i = 0; i < image.size(); i++) ASSERT_EQUALS(image[i], image2[i]);
2333   }
2334 
2335   // grayscale ICC profile, using an input image with grayscale colors but that
2336   // would normally benefit from a palette (which auto_convert would normally
2337   // choose). But the PNG spec does not allow combining palette with GRAY ICC
2338   // profile, so the encoder should not choose to use palette after all.
2339   {
2340     unsigned error;
2341     unsigned w = 32, h = 32;
2342     std::vector<unsigned char> image(w * h * 4);
2343     int colors[3] = {0, 3, 133};
2344     for(size_t i = 0; i + 4 <= image.size(); i += 4) {
2345       image[i] = image[i + 1] = image[i + 2] = image[i + 3] = colors[(i / 4) % 3];
2346     }
2347     std::vector<unsigned char> png;
2348     lodepng::State state;
2349     state.info_png.iccp_defined = 1;
2350     std::string testprofile = "0123456789abcdefGRAYfake iccp profile for testing";
2351     testprofile[0] = testprofile[1] = 0;
2352     lodepng_set_icc(&state.info_png, "test", (const unsigned char*)testprofile.c_str(), testprofile.size());
2353     error = lodepng::encode(png, &image[0], w, h, state);
2354     ASSERT_NO_PNG_ERROR(error);
2355 
2356     lodepng::State state2;
2357     std::vector<unsigned char> image2;
2358     error = lodepng::decode(image2, w, h, state2, png);
2359     ASSERT_NO_PNG_ERROR(error);
2360     assertColorProfileDataEqual(state, state2);
2361     ASSERT_NOT_EQUALS(LCT_PALETTE, state2.info_png.color.colortype);
2362   }
2363 
2364   // RGB ICC profile, using an input image with grayscale colors: the encoder
2365   // is forced to choose an RGB color type anyway with auto_convert
2366   {
2367     unsigned error;
2368     unsigned w = 32, h = 32;
2369     std::vector<unsigned char> image(w * h * 4);
2370     for(size_t i = 0; i + 4 <= image.size(); i += 4) {
2371       image[i] = image[i + 1] = image[i + 2] = (i / 4) & 255;
2372       image[i + 3] = 255;
2373     }
2374     std::vector<unsigned char> png;
2375     lodepng::State state;
2376     state.info_png.iccp_defined = 1;
2377     std::string testprofile = "0123456789abcdefRGB fake iccp profile for testing";
2378     testprofile[0] = testprofile[1] = 0;
2379     lodepng_set_icc(&state.info_png, "test", (const unsigned char*)testprofile.c_str(), testprofile.size());
2380     error = lodepng::encode(png, &image[0], w, h, state);
2381     ASSERT_NO_PNG_ERROR(error);
2382 
2383     lodepng::State state2;
2384     std::vector<unsigned char> image2;
2385     error = lodepng::decode(image2, w, h, state2, png);
2386     ASSERT_NO_PNG_ERROR(error);
2387     assertColorProfileDataEqual(state, state2);
2388     // LCT_RGB or LCT_PALETTE are both ok, gray is not (it likely chooses palette in practice)
2389     ASSERT_NOT_EQUALS(LCT_GREY, state2.info_png.color.colortype);
2390     ASSERT_NOT_EQUALS(LCT_GREY_ALPHA, state2.info_png.color.colortype);
2391   }
2392 
2393   // Encoder must give error when forcing invalid combination of color/gray
2394   // PNG with gray/color ICC Profile
2395   {
2396     unsigned error;
2397     unsigned w = 32, h = 32;
2398     std::vector<unsigned char> image(w * h * 4);
2399     int colors[3] = {0, 5, 33};
2400     for(size_t i = 0; i + 4 <= image.size(); i += 4) {
2401       image[i] = 255;
2402       image[i + 1] = image[i + 2] = image[i + 3] = colors[(i / 4) % 3];
2403     }
2404     std::vector<unsigned char> png;
2405     lodepng::State state;
2406     state.info_png.iccp_defined = 1;
2407     std::string testprofile = "0123456789abcdefGRAYfake iccp profile for testing";
2408     testprofile[0] = testprofile[1] = 0;
2409     lodepng_set_icc(&state.info_png, "test", (const unsigned char*)testprofile.c_str(), testprofile.size());
2410     error = lodepng::encode(png, &image[0], w, h, state);
2411     ASSERT_NOT_EQUALS(0, error);  // must give error due to color image input with gray profile
2412   }
2413 }
2414 
2415 // r, g, b is input background color to encoder, given in png color model
2416 // r2, g2, b2 is expected decoded background color, in color model it auto chose if auto_convert is on
2417 // pixels must be given in mode_raw color format
testBkgdChunk(unsigned r,unsigned g,unsigned b,unsigned r2,unsigned g2,unsigned b2,const std::vector<unsigned char> & pixels,unsigned w,unsigned h,const LodePNGColorMode & mode_raw,const LodePNGColorMode & mode_png,bool auto_convert,bool expect_encoder_error=false)2418 void testBkgdChunk(unsigned r, unsigned g, unsigned b,
2419                    unsigned r2, unsigned g2, unsigned b2,
2420                    const std::vector<unsigned char>& pixels,
2421                    unsigned w, unsigned h,
2422                    const LodePNGColorMode& mode_raw,
2423                    const LodePNGColorMode& mode_png,
2424                    bool auto_convert, bool expect_encoder_error = false) {
2425   unsigned error;
2426 
2427   lodepng::State state;
2428   LodePNGInfo& info = state.info_png;
2429   lodepng_color_mode_copy(&info.color, &mode_png);
2430   lodepng_color_mode_copy(&state.info_raw, &mode_raw);
2431   state.encoder.auto_convert = auto_convert;
2432 
2433   info.background_defined = 1;
2434   info.background_r = r;
2435   info.background_g = g;
2436   info.background_b = b;
2437 
2438   std::vector<unsigned char> png;
2439   error = lodepng::encode(png, pixels, w, h, state);
2440   if(expect_encoder_error) {
2441     ASSERT_NOT_EQUALS(0, error);
2442     return;
2443   }
2444   ASSERT_NO_PNG_ERROR(error);
2445 
2446   lodepng::State state2;
2447   LodePNGInfo& info2 = state2.info_png;
2448   state2.info_raw.colortype = LCT_RGBA;
2449   state2.info_raw.bitdepth = 16;
2450   unsigned w2, h2;
2451   std::vector<unsigned char> image2;
2452   error = lodepng::decode(image2, w2, h2, state2, &png[0], png.size());
2453   ASSERT_NO_PNG_ERROR(error);
2454 
2455   ASSERT_EQUALS(w, w2);
2456   ASSERT_EQUALS(h, h2);
2457   ASSERT_EQUALS(1, info2.background_defined);
2458   ASSERT_EQUALS(r2, info2.background_r);
2459   ASSERT_EQUALS(g2, info2.background_g);
2460   ASSERT_EQUALS(b2, info2.background_b);
2461 
2462   // compare pixels in the "raw" color model
2463   LodePNGColorMode mode_temp; lodepng_color_mode_init(&mode_temp); mode_temp.bitdepth = 16; mode_temp.colortype = LCT_RGBA;
2464   std::vector<unsigned char> image3((w * h * lodepng_get_bpp(&mode_raw) + 7) / 8);
2465   error = lodepng_convert(image3.data(), image2.data(), &mode_raw, &mode_temp, w, h);
2466   ASSERT_NO_PNG_ERROR(error);
2467   ASSERT_EQUALS(pixels.size(), image3.size());
2468   for(size_t i = 0; i < image3.size(); i++) {
2469     ASSERT_EQUALS((int)image3[i], (int)pixels[i]);
2470   }
2471 }
2472 
2473 // r, g, b is input background color to encoder, given in png color model
2474 // r2, g2, b2 is expected decoded background color, in color model it auto chose if auto_convert is on
testBkgdChunk(unsigned r,unsigned g,unsigned b,unsigned r2,unsigned g2,unsigned b2,LodePNGColorType type_pixels,unsigned bitdepth_pixels,LodePNGColorType type_raw,unsigned bitdepth_raw,LodePNGColorType type_png,unsigned bitdepth_png,bool auto_convert,bool expect_encoder_error=false)2475 void testBkgdChunk(unsigned r, unsigned g, unsigned b,
2476                    unsigned r2, unsigned g2, unsigned b2,
2477                    LodePNGColorType type_pixels, unsigned bitdepth_pixels,
2478                    LodePNGColorType type_raw, unsigned bitdepth_raw,
2479                    LodePNGColorType type_png, unsigned bitdepth_png,
2480                    bool auto_convert, bool expect_encoder_error = false) {
2481   unsigned error;
2482   Image image;
2483   generateTestImageRequiringColorType16(image, type_pixels, bitdepth_pixels, false);
2484 
2485   LodePNGColorMode mode_raw; lodepng_color_mode_init(&mode_raw); mode_raw.bitdepth = bitdepth_raw; mode_raw.colortype = type_raw;
2486   LodePNGColorMode mode_temp; lodepng_color_mode_init(&mode_temp); mode_temp.bitdepth = 16; mode_temp.colortype = LCT_RGBA;
2487   LodePNGColorMode mode_png; lodepng_color_mode_init(&mode_png); mode_png.bitdepth = bitdepth_png; mode_png.colortype = type_png;
2488   std::vector<unsigned char> temp((image.width * image.height * lodepng_get_bpp(&mode_raw) + 7) / 8);
2489   error = lodepng_convert(temp.data(), image.data.data(), &mode_raw, &mode_temp, image.width, image.height);
2490   ASSERT_NO_PNG_ERROR(error);
2491   image.data = temp;
2492 
2493   testBkgdChunk(r, g, b, r2, g2, b2,
2494                 image.data, image.width, image.height,
2495                 mode_raw, mode_png, auto_convert, expect_encoder_error);
2496 }
2497 
testBkgdChunk()2498 void testBkgdChunk() {
2499   std::cout << "testBkgdChunk" << std::endl;
2500   // color param order is: generated, raw, png ( == bKGD)
2501   // here generated means: what color values the pixels will get, so what auto_convert will make it choose
2502   testBkgdChunk(255, 0, 0, 255, 0, 0, LCT_RGBA, 8, LCT_RGBA, 8, LCT_RGBA, 8, true);
2503   testBkgdChunk(255, 0, 0, 255, 0, 0, LCT_RGBA, 8, LCT_RGB, 8, LCT_RGB, 8, true);
2504   testBkgdChunk(255, 0, 0, 255, 0, 0, LCT_RGB, 8, LCT_RGB, 8, LCT_RGB, 8, true);
2505   testBkgdChunk(255, 255, 255, 1, 1, 1, LCT_GREY, 1, LCT_RGB, 8, LCT_RGB, 8, true);
2506   testBkgdChunk(255, 255, 255, 3, 3, 3, LCT_GREY, 2, LCT_RGB, 8, LCT_RGB, 8, true);
2507   testBkgdChunk(255, 255, 255, 15, 15, 15, LCT_GREY, 4, LCT_RGB, 8, LCT_RGB, 8, true);
2508   testBkgdChunk(255, 255, 255, 255, 255, 255, LCT_GREY, 8, LCT_RGB, 8, LCT_RGB, 8, true);
2509   testBkgdChunk(255, 255, 255, 65535, 65535, 65535, LCT_GREY, 16, LCT_RGB, 16, LCT_RGB, 8, true);
2510   testBkgdChunk(123, 0, 0, 123, 0, 0, LCT_GREY, 1, LCT_RGB, 8, LCT_RGB, 8, true);
2511   testBkgdChunk(170, 170, 170, 2, 2, 2, LCT_GREY, 1, LCT_RGB, 8, LCT_RGB, 8, true); // 170 = value 2 in 2-bit
2512 
2513   // without auto_convert. Note that it will still convert if different colortype is given for raw and png, it's just
2514   // not automatic in that case.
2515   testBkgdChunk(255, 0, 0, 255, 0, 0, LCT_RGBA, 8, LCT_RGBA, 8, LCT_RGBA, 8, false);
2516   testBkgdChunk(60000, 0, 0, 60000, 0, 0, LCT_RGBA, 8, LCT_RGBA, 8, LCT_RGBA, 16, false);
2517   testBkgdChunk(128, 128, 128, 128, 128, 128, LCT_GREY, 8, LCT_RGBA, 8, LCT_GREY, 8, false);
2518  {
2519     LodePNGColorMode pal;
2520     lodepng_color_mode_init(&pal);
2521     for(int i = 0; i < 200; i++) lodepng_palette_add(&pal, i, i / 2, 0, 255);
2522     pal.colortype = LCT_PALETTE;
2523     pal.bitdepth = 8;
2524     unsigned w = 200;
2525     unsigned h = 200;
2526     std::vector<unsigned char> img(w * h);
2527     for(unsigned y = 0; y < h; y++)
2528     for(unsigned x = 0; x < w; x++) {
2529       img[y * w + x] = x;
2530     }
2531 
2532     testBkgdChunk(100, 0, 0, 100, 100, 100, img, w, h, pal, pal, true, false);
2533     testBkgdChunk(100, 0, 0, 100, 100, 100, img, w, h, pal, pal, false, false);
2534     testBkgdChunk(250, 0, 0, 250, 250, 250, img, w, h, pal, pal, true, true);
2535 
2536     std::vector<unsigned char> fourcolor(w * h);
2537     for(unsigned y = 0; y < h; y++)
2538     for(unsigned x = 0; x < w; x++) {
2539       fourcolor[y * w + x] = x & 3;
2540     }
2541     // palette index 4 expected for output bKGD: auto_convert should turn the 200-sized
2542     // palette in one of size 5, 4 values for the fourcolor image above, and then a 5th for
2543     // the bkgd index. The other two 4's actually shouldn't matter, it's not defined what
2544     // they should be though currently lodepng sets them also to the palette index...
2545     testBkgdChunk(100, 0, 0, 4, 4, 4, fourcolor, w, h, pal, pal, true, false);
2546 
2547 
2548     std::vector<unsigned char> mini(4);
2549     mini[0] = 1; mini[1] = 2; mini[2] = 3; mini[3] = 4;
2550     // here we expect RGB color from the output image, since the image is tiny so it chooses to not add PLTE
2551     testBkgdChunk(100, 0, 0, 100, 50, 0, mini, 2, 2, pal, pal, true, false);
2552 
2553     lodepng_color_mode_cleanup(&pal);
2554   }
2555 }
2556 
testBkgdChunk2()2557 void testBkgdChunk2() {
2558   std::cout << "testBkgdChunk2" << std::endl;
2559   Image image;
2560   generateTestImageRequiringColorType8(image, LCT_GREY, 2, false);
2561 
2562   // without background, it should choose 2-bit gray for this PNG
2563   std::vector<unsigned char> png0;
2564   ASSERT_NO_PNG_ERROR(lodepng::encode(png0, image.data, image.width, image.height));
2565   lodepng::State state0;
2566   unsigned w0, h0;
2567   lodepng_inspect(&w0, &h0, &state0, png0.data(), png0.size());
2568   ASSERT_EQUALS(2, state0.info_png.color.bitdepth);
2569   ASSERT_EQUALS(LCT_GREY, state0.info_png.color.colortype);
2570 
2571   // red background, with auto_convert, it is forced to choose RGB
2572   lodepng::State state;
2573   LodePNGInfo& info = state.info_png;
2574   info.background_defined = 1;
2575   info.background_r = 255;
2576   info.background_g = 0;
2577   info.background_b = 0;
2578   std::vector<unsigned char> png1;
2579   ASSERT_NO_PNG_ERROR(lodepng::encode(png1, image.data, image.width, image.height, state));
2580   lodepng::State state1;
2581   unsigned w1, h1;
2582   lodepng_inspect(&w1, &h1, &state1, png1.data(), png1.size());
2583   ASSERT_EQUALS(8, state1.info_png.color.bitdepth);
2584   ASSERT_EQUALS(LCT_RGB, state1.info_png.color.colortype);
2585 
2586   // gray output required, background color also interpreted as gray
2587   state.info_raw.colortype = LCT_RGB;
2588   state.info_png.color.colortype = LCT_GREY;
2589   state.info_png.color.bitdepth = 1;
2590   state.encoder.auto_convert = 0;
2591   info.background_defined = 1;
2592   info.background_r = 1;
2593   info.background_g = 1;
2594   info.background_b = 1;
2595   std::vector<unsigned char> png2;
2596   ASSERT_NO_PNG_ERROR(lodepng::encode(png2, image.data, image.width, image.height, state));
2597   lodepng::State state2;
2598   unsigned w2, h2;
2599   lodepng_inspect(&w2, &h2, &state2, png2.data(), png2.size());
2600   ASSERT_EQUALS(1, state2.info_png.color.bitdepth);
2601   ASSERT_EQUALS(LCT_GREY, state2.info_png.color.colortype);
2602 }
2603 
2604 // Test particular cHRM+gAMA conversion to srgb
2605 // gamma = gamma given 100000x multiplied form of PNG, or 0 to set none at all
2606 // wx..by = whitepoint and chromaticities, given in the 100000x multiplied form of PNG
2607 // r, g, b: r, g, b values to encode in the PNG's data
2608 // er, eg, eb: expected r, g, b values after decoding and converting to sRGB
testChrmToSrgb(unsigned gamma,unsigned wx,unsigned wy,unsigned rx,unsigned ry,unsigned gx,unsigned gy,unsigned bx,unsigned by,unsigned char r,unsigned char g,unsigned char b,unsigned char er,unsigned char eg,unsigned char eb,int max_dist=0)2609 void testChrmToSrgb(unsigned gamma, unsigned wx, unsigned wy, unsigned rx, unsigned ry, unsigned gx, unsigned gy, unsigned bx, unsigned by,
2610                     unsigned char r, unsigned char g, unsigned char b, unsigned char er, unsigned char eg, unsigned char eb,
2611                     int max_dist = 0) {
2612   std::vector<unsigned char> image(4);
2613   image[0] = r;
2614   image[1] = g;
2615   image[2] = b;
2616   image[3] = 255;
2617   lodepng::State state;
2618   if(gamma) {
2619     state.info_png.gama_defined = 1;
2620     state.info_png.gama_gamma = gamma;
2621   }
2622   state.info_png.chrm_defined = 1;
2623   state.info_png.chrm_white_x = wx;
2624   state.info_png.chrm_white_y = wy;
2625   state.info_png.chrm_red_x = rx;
2626   state.info_png.chrm_red_y = ry;
2627   state.info_png.chrm_green_x = gx;
2628   state.info_png.chrm_green_y = gy;
2629   state.info_png.chrm_blue_x = bx;
2630   state.info_png.chrm_blue_y = by;
2631 
2632   std::vector<unsigned char> image2(4);
2633   convertToSrgb(image2.data(), image.data(), 1, 1, &state);
2634 
2635   if(max_dist == 0) {
2636     ASSERT_EQUALS(er, image2[0]);
2637     ASSERT_EQUALS(eg, image2[1]);
2638     ASSERT_EQUALS(eb, image2[2]);
2639   } else {
2640     ASSERT_NEAR(er, image2[0], max_dist);
2641     ASSERT_NEAR(eg, image2[1], max_dist);
2642     ASSERT_NEAR(eb, image2[2], max_dist);
2643   }
2644 
2645   // Also test the opposite direction
2646 
2647   std::vector<unsigned char> image3(4);
2648   convertFromSrgb(image3.data(), image2.data(), 1, 1, &state);
2649 
2650   if(max_dist == 0) {
2651     ASSERT_EQUALS(r, image3[0]);
2652     ASSERT_EQUALS(g, image3[1]);
2653     ASSERT_EQUALS(b, image3[2]);
2654   } else {
2655     ASSERT_NEAR(r, image3[0], max_dist);
2656     ASSERT_NEAR(g, image3[1], max_dist);
2657     ASSERT_NEAR(b, image3[2], max_dist);
2658   }
2659 }
2660 
testChrmToSrgb()2661 void testChrmToSrgb() {
2662   std::cout << "testChrmToSrgb" << std::endl;
2663   // srgb gamma approximation and chromaticities defined as standard by png (multiplied by 100000)
2664   int sg = 45455; // srgb gamma approximation
2665   (void)sg;
2666   int swx = 31270;
2667   int swy = 32900;
2668   int srx = 64000;
2669   int sry = 33000;
2670   int sgx = 30000;
2671   int sgy = 60000;
2672   int sbx = 15000;
2673   int sby = 6000;
2674 
2675   testChrmToSrgb(sg, swx, swy, srx, sry, sgx, sgy, sbx, sby, 0, 0, 0, 0, 0, 0);
2676   testChrmToSrgb(sg, swx, swy, srx, sry, sgx, sgy, sbx, sby, 255, 255, 255, 255, 255, 255);
2677 
2678   testChrmToSrgb(0, swx, swy, srx, sry, sgx, sgy, sbx, sby, 50, 50, 50, 50, 50, 50);
2679   testChrmToSrgb(0, swx, swy, srx, sry, sgx, sgy, sbx, sby, 128, 128, 128, 128, 128, 128);
2680   testChrmToSrgb(0, swx, swy, srx, sry, sgx, sgy, sbx, sby, 200, 200, 200, 200, 200, 200);
2681 
2682   testChrmToSrgb(0, swx, swy, srx, sry, sgx, sgy, sbx, sby, 255, 0, 0, 255, 0, 0);
2683   testChrmToSrgb(0, swx, swy, srx, sry, sgx, sgy, sbx, sby, 0, 255, 0, 0, 255, 0);
2684   testChrmToSrgb(0, swx, swy, srx, sry, sgx, sgy, sbx, sby, 0, 0, 255, 0, 0, 255);
2685 
2686   // swap red and green chromaticities
2687   testChrmToSrgb(0, swx, swy, sgx, sgy, srx, sry, sbx, sby, 255, 0, 0, 0, 255, 0);
2688   testChrmToSrgb(0, swx, swy, sgx, sgy, srx, sry, sbx, sby, 0, 255, 0, 255, 0, 0);
2689   testChrmToSrgb(0, swx, swy, sgx, sgy, srx, sry, sbx, sby, 0, 0, 255, 0, 0, 255);
2690 
2691   // swap red/green/blue chromaticities
2692   testChrmToSrgb(0, swx, swy, sgx, sgy, sbx, sby, srx, sry, 255, 0, 0, 0, 255, 0);
2693   testChrmToSrgb(0, swx, swy, sgx, sgy, sbx, sby, srx, sry, 0, 255, 0, 0, 0, 255);
2694   testChrmToSrgb(0, swx, swy, sgx, sgy, sbx, sby, srx, sry, 0, 0, 255, 255, 0, 0);
2695 
2696   // different whitepoint does not affect white or gray, due to the relative rendering intent (adaptation)
2697   testChrmToSrgb(0, 35000, 25000, srx, sry, sgx, sgy, sbx, sby, 0, 0, 0, 0, 0, 0);
2698   testChrmToSrgb(0, 35000, 25000, srx, sry, sgx, sgy, sbx, sby, 50, 50, 50, 50, 50, 50);
2699   testChrmToSrgb(0, 35000, 25000, srx, sry, sgx, sgy, sbx, sby, 128, 128, 128, 128, 128, 128);
2700   testChrmToSrgb(0, 35000, 25000, srx, sry, sgx, sgy, sbx, sby, 200, 200, 200, 200, 200, 200);
2701   testChrmToSrgb(0, 35000, 25000, srx, sry, sgx, sgy, sbx, sby, 255, 255, 255, 255, 255, 255);
2702 }
2703 
2704 
2705 
testXYZ()2706 void testXYZ() {
2707   std::cout << "testXYZ" << std::endl;
2708   unsigned w = 512, h = 512;
2709   std::vector<unsigned char> v(w * h * 4 * 2);
2710   for(size_t i = 0; i < v.size(); i++) {
2711     v[i] = getRandom() & 255;
2712   }
2713 
2714   // Test sRGB -> XYZ -> sRGB roundtrip
2715 
2716   unsigned rendering_intent = 3; // test with absolute for now
2717 
2718   // 8-bit
2719   {
2720     // Default state, the conversions use 8-bit sRGB
2721     lodepng::State state;
2722     std::vector<float> f(w * h * 4);
2723     float whitepoint[3];
2724     assertNoError(lodepng::convertToXYZ(f.data(), whitepoint, v.data(), w, h, &state));
2725 
2726     std::vector<unsigned char> v2(w * h * 4);
2727     assertNoError(lodepng::convertFromXYZ(v2.data(), f.data(), w, h, &state, whitepoint, rendering_intent));
2728 
2729     for(size_t i = 0; i < v2.size(); i++) {
2730       ASSERT_EQUALS(v[i], v2[i]);
2731     }
2732   }
2733 
2734   // 16-bit
2735   {
2736     // Default state but with 16-bit, the conversions use 16-bit sRGB
2737     lodepng::State state;
2738     state.info_raw.bitdepth = 16;
2739     std::vector<float> f(w * h * 4);
2740     float whitepoint[3];
2741     assertNoError(lodepng::convertToXYZ(f.data(), whitepoint, v.data(), w, h, &state));
2742 
2743     std::vector<unsigned char> v2(w * h * 8);
2744     assertNoError(lodepng::convertFromXYZ(v2.data(), f.data(), w, h, &state, whitepoint, rendering_intent));
2745 
2746     for(size_t i = 0; i < v2.size(); i++) {
2747       ASSERT_EQUALS(v[i], v2[i]);
2748     }
2749   }
2750 
2751   // Test custom RGB+gamma -> XYZ -> custom RGB+gamma roundtrip
2752 
2753   LodePNGInfo info_custom;
2754   lodepng_info_init(&info_custom);
2755   info_custom.gama_defined = 1;
2756   info_custom.gama_gamma =   30000; // default 45455
2757   info_custom.chrm_defined = 1;
2758   info_custom.chrm_white_x = 10000; // default 31270
2759   info_custom.chrm_white_y = 20000; // default 32900
2760   info_custom.chrm_red_x =   30000; // default 64000
2761   info_custom.chrm_red_y =   50000; // default 33000
2762   info_custom.chrm_green_x = 70000; // default 30000
2763   info_custom.chrm_green_y = 11000; // default 60000
2764   info_custom.chrm_blue_x =  13000; // default 15000
2765   info_custom.chrm_blue_y =  17000; // default 6000
2766 
2767   // 8-bit
2768   {
2769     lodepng::State state;
2770     lodepng_info_copy(&state.info_png, &info_custom);
2771     std::vector<float> f(w * h * 4);
2772     float whitepoint[3];
2773     assertNoError(lodepng::convertToXYZ(f.data(), whitepoint, v.data(), w, h, &state));
2774 
2775     std::vector<unsigned char> v2(w * h * 4);
2776     assertNoError(lodepng::convertFromXYZ(v2.data(), f.data(), w, h, &state, whitepoint, rendering_intent));
2777 
2778     for(size_t i = 0; i < v2.size(); i++) {
2779       // Allow near instead of exact due to numerical issues with low values,
2780       // see description at the 16-bit test below.
2781       unsigned maxdist = 0;
2782       if(v[i] <= 2) maxdist = 3;
2783       else if(v[i] <= 4) maxdist = 2;
2784       else maxdist = 0;
2785       ASSERT_NEAR(v[i], v2[i], maxdist);
2786     }
2787   }
2788 
2789   // 16-bit
2790   {
2791     lodepng::State state;
2792     lodepng_info_copy(&state.info_png, &info_custom);
2793     state.info_raw.bitdepth = 16;
2794     std::vector<float> f(w * h * 4);
2795     float whitepoint[3];
2796     assertNoError(lodepng::convertToXYZ(f.data(), whitepoint, v.data(), w, h, &state));
2797 
2798     std::vector<unsigned char> v2(w * h * 8);
2799     assertNoError(lodepng::convertFromXYZ(v2.data(), f.data(), w, h, &state, whitepoint, rendering_intent));
2800 
2801     for(size_t i = 0; i < v2.size(); i += 2) {
2802       unsigned a = v[i + 0] * 256u + v[i + 1];
2803       unsigned a2 = v2[i + 0] * 256u + v2[i + 1];
2804       // There are numerical issues with low values due to the precision of float,
2805       // so allow some distance for low values (low compared to 65535).
2806       // The issue seems to be: the combination of how the gamma correction affects
2807       // low values and the color conversion matrix operating on single precision
2808       // floating point. With the sRGB's gamma the problem seems not to happen, maybe
2809       // because that linear part near 0 behaves better than power.
2810       // TODO: check if it can be fixed without using double for the image and without slow double precision pow.
2811       unsigned maxdist = 0;
2812       if(a < 2048) maxdist = 768;
2813       else if(a < 4096) maxdist = 24;
2814       else if(a < 16384) maxdist = 4;
2815       else maxdist = 2;
2816       ASSERT_NEAR(a, a2, maxdist);
2817     }
2818   }
2819 
2820   lodepng_info_cleanup(&info_custom);
2821 }
2822 
2823 
testICC()2824 void testICC() {
2825   std::cout << "testICC" << std::endl;
2826   // approximate srgb (gamma function not exact)
2827   std::string icc_near_srgb_base64 =
2828       "AAABwHRlc3QCQAAAbW50clJHQiBYWVogB+MAAQABAAAAAAAAYWNzcFNHSSAAAAABAAAAAAAAAAAA"
2829       "AAAAAAAAAAAAAAEAAPbWAAEAAAAA0y10ZXN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2830       "AAAAAAAAAAAAAAAAAAAAAAAJY3BydAAAAPAAAAANZGVzYwAAAQAAAABfd3RwdAAAAWAAAAAUclhZ"
2831       "WgAAAXQAAAAUZ1hZWgAAAYgAAAAUYlhZWgAAAZwAAAAUclRSQwAAAbAAAAAOZ1RSQwAAAbAAAAAO"
2832       "YlRSQwAAAbAAAAAOdGV4dAAAAABDQzAgAAAAAGRlc2MAAAAAAAAABXRlc3QAZW5VUwAAAAAAAAAA"
2833       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2834       "AAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAG+gAAA49AAAA5BYWVogAAAA"
2835       "AAAAYpYAALeHAAAY2VhZWiAAAAAAAAAkngAAD4QAALbCY3VydgAAAAAAAAABAjMAAA==";
2836   std::vector<unsigned char> icc_near_srgb;
2837   fromBase64(icc_near_srgb, icc_near_srgb_base64);
2838   lodepng::State state_near_srgb;
2839   lodepng_set_icc(&state_near_srgb.info_png, "near_srgb", icc_near_srgb.data(), icc_near_srgb.size());
2840 
2841   // a made up RGB model.
2842   // it causes (when converting from this to srgb) green to become softer green, blue to become softer blue, red to become orange.
2843   // this model intersects sRGB, but some parts are outside of sRGB, some parts of sRGB are outside of this one.
2844   // so when converting between this and sRGB and clipping the values to 8-bit, and then converting back, the values will not be the same due to this clipping
2845   std::string icc_orange_base64 =
2846       "AAABwHRlc3QCQAAAbW50clJHQiBYWVogB+MAAQABAAAAAAAAYWNzcFNHSSAAAAABAAAAAAAAAAAA"
2847       "AAAAAAAAAAAAAAMAAPbWAAEAAAAA0y10ZXN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2848       "AAAAAAAAAAAAAAAAAAAAAAAJY3BydAAAAPAAAAANZGVzYwAAAQAAAABfd3RwdAAAAWAAAAAUclhZ"
2849       "WgAAAXQAAAAUZ1hZWgAAAYgAAAAUYlhZWgAAAZwAAAAUclRSQwAAAbAAAAAOZ1RSQwAAAbAAAAAO"
2850       "YlRSQwAAAbAAAAAOdGV4dAAAAABDQzAgAAAAAGRlc2MAAAAAAAAABXRlc3QAZW5VUwAAAAAAAAAA"
2851       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2852       "AAAAAAAAAAAAAFhZWiAAAAAAAAE7uwABAAAAARmZWFlaIAAAAAAAANAHAACTTAAACrRYWVogAAAA"
2853       "AAAABOMAAFd4AAAFzVhZWiAAAAAAAAAh6gAAFTsAAMKqY3VydgAAAAAAAAABAoAAAA==";
2854   std::vector<unsigned char> icc_orange;
2855   fromBase64(icc_orange, icc_orange_base64);
2856   lodepng::State state_orange;
2857   lodepng_set_icc(&state_orange.info_png, "orange", icc_orange.data(), icc_orange.size());
2858 
2859   // A made up RGB model which is a superset of sRGB, and has R/G/B shifted around (so it greatly alters colors)
2860   // Since this is a superset of sRGB, converting from sRGB to this model, and then back, should be lossless, but the opposite not necessarily.
2861   std::string icc_super_base64 =
2862       "AAABwHRlc3QCQAAAbW50clJHQiBYWVogB+MAAQABAAAAAAAAYWNzcFNHSSAAAAABAAAAAAAAAAAA"
2863       "AAAAAAAAAAAAAAEAAPbWAAEAAAAA0y10ZXN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2864       "AAAAAAAAAAAAAAAAAAAAAAAJY3BydAAAAPAAAAANZGVzYwAAAQAAAABfd3RwdAAAAWAAAAAUclhZ"
2865       "WgAAAXQAAAAUZ1hZWgAAAYgAAAAUYlhZWgAAAZwAAAAUclRSQwAAAbAAAAAOZ1RSQwAAAbAAAAAO"
2866       "YlRSQwAAAbAAAAAOdGV4dAAAAABDQzAgAAAAAGRlc2MAAAAAAAAABXRlc3QAZW5VUwAAAAAAAAAA"
2867       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2868       "AAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAFW+AADL3f//70ZYWVogAAAA"
2869       "AAAAJqD////UAADsUFhZWiAAAAAAAAB6dgAANE////eXY3VydgAAAAAAAAABAjMAAA==";
2870   std::vector<unsigned char> icc_super;
2871   fromBase64(icc_super, icc_super_base64);
2872   lodepng::State state_super;
2873   lodepng_set_icc(&state_super.info_png, "super", icc_super.data(), icc_super.size());
2874 
2875   // A made up RGB model which is a subset of sRGB, and has R/G/B shifted around (so it greatly alters colors)
2876   // Since this is a subset of sRGB, converting to sRGB from this model, and then back, should be lossless, but the opposite not necessarily.
2877   std::string icc_sub_base64 =
2878       "AAABwHRlc3QCQAAAbW50clJHQiBYWVogB+MAAQABAAAAAAAAYWNzcFNHSSAAAAABAAAAAAAAAAAA"
2879       "AAAAAAAAAAAAAAEAAPbWAAEAAAAA0y10ZXN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2880       "AAAAAAAAAAAAAAAAAAAAAAAJY3BydAAAAPAAAAANZGVzYwAAAQAAAABfd3RwdAAAAWAAAAAUclhZ"
2881       "WgAAAXQAAAAUZ1hZWgAAAYgAAAAUYlhZWgAAAZwAAAAUclRSQwAAAbAAAAAOZ1RSQwAAAbAAAAAO"
2882       "YlRSQwAAAbAAAAAOdGV4dAAAAABDQzAgAAAAAGRlc2MAAAAAAAAABXRlc3QAZW5VUwAAAAAAAAAA"
2883       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2884       "AAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAHEEAABy1AAAr8ZYWVogAAAA"
2885       "AAAAV5kAAEPkAAAMs1hZWiAAAAAAAAAuNwAASUcAABazY3VydgAAAAAAAAABAjMAAA==";
2886 
2887   std::vector<unsigned char> icc_sub;
2888   fromBase64(icc_sub, icc_sub_base64);
2889   lodepng::State state_sub;
2890   lodepng_set_icc(&state_sub.info_png, "sub", icc_sub.data(), icc_sub.size());
2891 
2892   // make 8-pixel image with following colors: white, gray, red, darkred, green, darkgreen, blue, darkblue
2893   unsigned w = 4, h = 2;
2894   std::vector<unsigned char> im(w * h * 4, 255);
2895   im[0 * 4 + 0] = 255; im[0 * 4 + 1] = 255; im[0 * 4 + 2] = 255;
2896   im[1 * 4 + 0] = 128; im[1 * 4 + 1] = 128; im[1 * 4 + 2] = 128;
2897   im[2 * 4 + 0] = 255; im[2 * 4 + 1] =   0; im[2 * 4 + 2] =   0;
2898   im[3 * 4 + 0] = 128; im[3 * 4 + 1] =   0; im[3 * 4 + 2] =   0;
2899   im[4 * 4 + 0] =   0; im[4 * 4 + 1] = 255; im[4 * 4 + 2] =   0;
2900   im[5 * 4 + 0] =   0; im[5 * 4 + 1] = 128; im[5 * 4 + 2] =   0;
2901   im[6 * 4 + 0] =   0; im[6 * 4 + 1] =   0; im[6 * 4 + 2] = 255;
2902   im[7 * 4 + 0] =   0; im[7 * 4 + 1] =   0; im[7 * 4 + 2] = 128;
2903 
2904 
2905   {
2906     std::vector<unsigned char> im2(w * h * 4, 255);
2907     assertNoError(convertToSrgb(im2.data(), im.data(), w, h, &state_orange));
2908 
2909     ASSERT_NEAR(255, im2[0 * 4 + 0], 1); ASSERT_NEAR(255, im2[0 * 4 + 1], 1); ASSERT_NEAR(255, im2[0 * 4 + 2], 1);
2910     ASSERT_NEAR(117, im2[1 * 4 + 0], 1); ASSERT_NEAR(117, im2[1 * 4 + 1], 1); ASSERT_NEAR(117, im2[1 * 4 + 2], 1);
2911     ASSERT_NEAR(255, im2[2 * 4 + 0], 1); ASSERT_NEAR(151, im2[2 * 4 + 1], 1); ASSERT_NEAR(  0, im2[2 * 4 + 2], 1);
2912     ASSERT_NEAR(145, im2[3 * 4 + 0], 1); ASSERT_NEAR( 66, im2[3 * 4 + 1], 1); ASSERT_NEAR(  0, im2[3 * 4 + 2], 1);
2913     ASSERT_NEAR(  0, im2[4 * 4 + 0], 1); ASSERT_NEAR(209, im2[4 * 4 + 1], 1); ASSERT_NEAR(  0, im2[4 * 4 + 2], 1);
2914     ASSERT_NEAR(  0, im2[5 * 4 + 0], 1); ASSERT_NEAR( 95, im2[5 * 4 + 1], 1); ASSERT_NEAR(  0, im2[5 * 4 + 2], 1);
2915     ASSERT_NEAR(  0, im2[6 * 4 + 0], 1); ASSERT_NEAR( 66, im2[6 * 4 + 1], 1); ASSERT_NEAR(255, im2[6 * 4 + 2], 1);
2916     ASSERT_NEAR(  0, im2[7 * 4 + 0], 1); ASSERT_NEAR( 25, im2[7 * 4 + 1], 1); ASSERT_NEAR(120, im2[7 * 4 + 2], 1);
2917 
2918     // Cannot test the inverse direction to see if same as original, because the color model here has values
2919     // outside of sRGB so several values were clipped.
2920   }
2921 
2922   {
2923     std::vector<unsigned char> im2(w * h * 4, 255);
2924     // convert between the two in one and then the other direction
2925     assertNoError(convertRGBModel(im2.data(), im.data(), w, h, &state_near_srgb, &state_sub, 3));
2926     std::vector<unsigned char> im3(w * h * 4, 255);
2927     assertNoError(convertRGBModel(im3.data(), im2.data(), w, h, &state_sub, &state_near_srgb, 3));
2928     // im3 should be same as im (allow some numerical errors), because we converted from a subset of sRGB to sRGB
2929     // and then back.
2930     // If state_super was used here instead (with a superset RGB color model), the test below would faill due to
2931     // the clipping of the values in the 8-bit chars (due to the superset being out of range for sRGB)
2932     for(size_t i = 0; i < im.size(); i++) {
2933       // due to the gamma (trc), small values are very imprecise (due to the 8-bit char step in between), so allow more distance there
2934       int tolerance = im[i] < 32 ? 16 : 1;
2935       ASSERT_NEAR(im[i], im3[i], tolerance);
2936     }
2937   }
2938 
2939   {
2940     std::vector<unsigned char> im2(w * h * 4, 255);
2941     assertNoError(convertFromSrgb(im2.data(), im.data(), w, h, &state_super));
2942     std::vector<unsigned char> im3(w * h * 4, 255);
2943     assertNoError(convertToSrgb(im3.data(), im2.data(), w, h, &state_super));
2944     for(size_t i = 0; i < im.size(); i++) {
2945       int tolerance = im[i] < 32 ? 16 : 1;
2946       ASSERT_NEAR(im[i], im3[i], tolerance);
2947     }
2948   }
2949 }
2950 
2951 
testICCGray()2952 void testICCGray() {
2953   std::cout << "testICCGray" << std::endl;
2954   // Grayscale, Gamma 2.2, sRGB whitepoint
2955   std::string icc22_base64 =
2956       "AAABSHRlc3QCQAAAbW50ckdSQVlYWVogB+MAAQABAAAAAAAAYWNzcFNHSSAAAAABAAAAAAAAAAAA"
2957       "AAAAAAAAAAAAAAMAAPbWAAEAAAAA0y10ZXN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2958       "AAAAAAAAAAAAAAAAAAAAAAAEY3BydAAAALQAAAANZGVzYwAAAMQAAABfd3RwdAAAASQAAAAUa1RS"
2959       "QwAAATgAAAAOdGV4dAAAAABDQzAgAAAAAGRlc2MAAAAAAAAABXRlc3QAZW5VUwAAAAAAAAAAAAAA"
2960       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2961       "AAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAABAjMAAA==";
2962   std::vector<unsigned char> icc22;
2963   fromBase64(icc22, icc22_base64);
2964   lodepng::State state22;
2965   state22.info_raw.colortype = LCT_GREY;
2966   lodepng_set_icc(&state22.info_png, "gray22", icc22.data(), icc22.size());
2967 
2968   // Grayscale, Gamma 2.9, custom whitepoint
2969   std::string icc29_base64 =
2970       "AAABSHRlc3QCQAAAbW50ckdSQVlYWVogB+MAAQABAAAAAAAAYWNzcFNHSSAAAAABAAAAAAAAAAAA"
2971       "AAAAAAAAAAAAAAMAAPbWAAEAAAAA0y10ZXN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2972       "AAAAAAAAAAAAAAAAAAAAAAAEY3BydAAAALQAAAANZGVzYwAAAMQAAABfd3RwdAAAASQAAAAUa1RS"
2973       "QwAAATgAAAAOdGV4dAAAAABDQzAgAAAAAGRlc2MAAAAAAAAABXRlc3QAZW5VUwAAAAAAAAAAAAAA"
2974       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2975       "AAAAAAAAAFhZWiAAAAAAAAE7uwABAAAAARmZY3VydgAAAAAAAAABAuYAAA==";
2976   std::vector<unsigned char> icc29;
2977   fromBase64(icc29, icc29_base64);
2978   lodepng::State state29;
2979   state29.info_raw.colortype = LCT_GREY;
2980   lodepng_set_icc(&state29.info_png, "gray29", icc29.data(), icc29.size());
2981 
2982   // Grayscale, Gamma 1.5, custom whitepoint
2983   std::string icc15_base64 =
2984       "AAABSHRlc3QCQAAAbW50ckdSQVlYWVogB+MAAQABAAAAAAAAYWNzcFNHSSAAAAABAAAAAAAAAAAA"
2985       "AAAAAAAAAAAAAAMAAPbWAAEAAAAA0y10ZXN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2986       "AAAAAAAAAAAAAAAAAAAAAAAEY3BydAAAALQAAAANZGVzYwAAAMQAAABfd3RwdAAAASQAAAAUa1RS"
2987       "QwAAATgAAAAOdGV4dAAAAABDQzAgAAAAAGRlc2MAAAAAAAAABXRlc3QAZW5VUwAAAAAAAAAAAAAA"
2988       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2989       "AAAAAAAAAFhZWiAAAAAAAAE7uwABAAAAARmZY3VydgAAAAAAAAABAYAAAA==";
2990   std::vector<unsigned char> icc15;
2991   fromBase64(icc15, icc15_base64);
2992   lodepng::State state15;
2993   state15.info_raw.colortype = LCT_GREY;
2994   lodepng_set_icc(&state15.info_png, "gray15", icc15.data(), icc15.size());
2995 
2996 
2997   // make 8-pixel grayscale image with different shades of gray
2998   unsigned w = 4, h = 2;
2999   std::vector<unsigned char> im(w * h, 255);
3000   im[0] = 0;
3001   im[1] = 40;
3002   im[2] = 80;
3003   im[3] = 120;
3004   im[4] = 160;
3005   im[5] = 200;
3006   im[6] = 240;
3007   im[7] = 255;
3008 
3009   {
3010     std::vector<unsigned char> im2(w * h, 255);
3011     assertNoError(convertToSrgb(im2.data(), im.data(), w, h, &state29));
3012 
3013     ASSERT_NEAR(0, im2[0], 1);
3014     ASSERT_NEAR(15, im2[1], 1);
3015     ASSERT_NEAR(52, im2[2], 1);
3016     ASSERT_NEAR(94, im2[3], 1);
3017     ASSERT_NEAR(139, im2[4], 1);
3018     ASSERT_NEAR(187, im2[5], 1);
3019     ASSERT_NEAR(236, im2[6], 1);
3020     ASSERT_NEAR(255, im2[7], 1);
3021 
3022     std::vector<unsigned char> im3(w * h, 255);
3023     assertNoError(convertFromSrgb(im3.data(), im2.data(), w, h, &state29));
3024 
3025     for(size_t i = 0; i < 8; i++) {
3026       ASSERT_NEAR(im[i], im3[i], 1);
3027     }
3028   }
3029 
3030   {
3031     std::vector<unsigned char> im2(w * h , 255);
3032     assertNoError(convertRGBModel(im2.data(), im.data(), w, h, &state22, &state15, 3));
3033     std::vector<unsigned char> im3(w * h, 255);
3034     assertNoError(convertRGBModel(im3.data(), im2.data(), w, h, &state15, &state22, 3));
3035     for(size_t i = 0; i < im.size(); i++) {
3036       int tolerance = im[i] < 16 ? 8 : 1;
3037       ASSERT_NEAR(im[i], im3[i], tolerance);
3038     }
3039   }
3040 }
3041 
3042 // input is base64-encoded png image and base64-encoded RGBA pixels (8 bit per channel)
testPNGSuiteImage(const std::string & name,bool expect_error,const std::string & png64,const std::string & pixels64)3043 void testPNGSuiteImage(const std::string& name, bool expect_error, const std::string& png64, const std::string& pixels64) {
3044   std::cout << "PngSuite: " << name << std::endl;
3045   std::vector<unsigned char> png, pixels;
3046   fromBase64(png, png64);
3047   fromBase64(pixels, pixels64);
3048 
3049   std::vector<unsigned char> decoded;
3050   unsigned w, h;
3051   unsigned error = lodepng::decode(decoded, w, h, png);
3052   if(expect_error) {
3053     ASSERT_EQUALS(true, error != 0);
3054     return;
3055   }
3056 
3057   assertNoError(error);
3058   ASSERT_EQUALS(pixels, decoded);
3059 }
3060 
testPNGSuite()3061 void testPNGSuite() {
3062   /*
3063   LICENSE of the PngSuite images:
3064 
3065   PngSuite
3066   --------
3067 
3068   Permission to use, copy, modify and distribute these images for any
3069   purpose and without fee is hereby granted.
3070 
3071 
3072   (c) Willem van Schaik, 1996, 2011
3073   */
3074 
3075   // For now, to keep it reasonable in here, only a few of the smaller images are tested
3076   // TODO: use a checksum of the pixels instead to avoid the huge base64 strings
3077 
3078   testPNGSuiteImage("cdfn2c08.png", false,
3079       "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAgCAIAAACgkq6HAAAABGdBTUEAAYagMeiWXwAAAANzQklUBAQEd/i1owAAAAlwSFlzAAAAAQAAAAQAMlIwkwAAASdJREFUeJxV0mFx7DAMBOCvmRAQBVMIhVAIhaNwFPogvFI4CA2FQGggVBDcH3acNpMZja1daVfWWwWClWRvZ3OPGwGSA6YOjw5QepxgcX+lg2ZYKc7BXNip1G9f1bP6X9WqvqtMregBTk691NTCcbUYCXX19d0qbuqyVvVTDZNwdjWFJS+/c/PEw/5QZNnTGa1HIsNHeAUlEc0gztheyguRLlWN8XRs2Vv0Hm12xRGt5j2rS/qY753w6+oPI+OefuPNA/KyHuISZZYCJf+VyKVrlINR8nyw3I95MRy2XeCMwSiwK0FGSwxGODk4yyV8lgpP0o612UlTU3EtjXL5nNozrQTLr8RbxZMix9Z9cDQfxz1B2kK0WY0dabc5EtlRf0B1/Ku63McfFzN1pnMg8LcAAAAASUVORK5CYII=",
3080       "/wAA//8AAP//EQD//1UA//9EAP//AAD//wAA//8AAP//AAD//wAA//9mAP//dwD//3cA//8zAP//AAD//wAA//8AAP//EQD//3cA//93AP//dwD//1UA//8AAP//AAD//wAA//9EAP//dwD//3cA//93AP//dwD//xEA//8AAP//AAD//4gA//+qAP/dqgD/7qoA//+qAP//RAD//wAA//8AAP//7gD/3f8A/3f/AP+Z/wD///8A//+IAP//AAD//yIA///uAP+q/wD/d/8A/3f/AP/d/wD//5kA//8AAP//MwD//+4A/3f/AP93/wD/d/8A/7v/AP//qgD//wAA//9EAP/d7gD/AP8A/wD/Vf8A/zP/RP8A//+7AP//EQD//1UA/7vuAP8A/yL/AP93/wD/Zv8z/wD//7sA//8iAP//VQD/qu4A/wD/iP8Au+7/AN3u/yL/Iv/uuwD//yIA//9mAP+Z7gD/AO6Z/wBV//8AiP//Iv9E/927AP//MwD//2YA/5nuAP8AzKr/VQD//yIz//8i/1X/zLsA//8zAP//dwD/iO4A/wC7u//MAMz/dyLu/yL/Vf/MuwD//0QA//93AP+I7gD/AKq7/+4Amf+IIsz/Iu5m/8y7AP//RAD//3cA/4juAP8Rqrv/7gCZ/5kizP8i7mb/u7sA//9EAP//dwD/iO4A/xGqu//uAJn/iCLM/yLuZv+7uwD//0QA//93AP+I7gD/ALu7/+4Aqv+IIt3/Iu5m/8y7AP//RAD//3cA/4juAP8Au7v/mQDu/1Ui//8i/1X/zLsA//9EAP//ZgD/me4A/wDdqv8iIv//EVX//yL/RP/MuwD//zMA//9mAP+Z7gD/AP+Z/wCI//8Au///Iv9E/927AP//MwD//1UA/6ruAP8A/2b/AN27/wDuu/8i/yL/7rsA//8iAP//VQD/zO4A/wD/Iv8A/3f/AP9m/zP/AP//uwD//yIA//9EAP/d7gD/Ef8A/xH/RP8R/yL/Vf8A//+7AP//EQD//zMA///uAP+I/wD/d/8A/3f/AP+7/wD//6oA//8AAP//IgD//+4A/6r/AP93/wD/d/8A/93/AP//mQD//wAA//8AAP//7gD/7v8A/4j/AP+q/wD///8A//+IAP//AAD//wAA//93AP//mQD/7pkA//+ZAP//mQD//zMA//8AAP//AAD//0QA//93AP//dwD//3cA//93AP//EQD//wAA//8AAP//EQD//3cA//93AP//dwD//1UA//8AAP//AAD//wAA//8AAP//VQD//3cA//93AP//IgD//wAA//8AAP//AAD//wAA//8AAP//RAD//zMA//8AAP//AAD//wAA/w==");
3081   testPNGSuiteImage("cdhn2c08.png", false,
3082       "iVBORw0KGgoAAAANSUhEUgAAACAAAAAICAIAAAAX52r4AAAABGdBTUEAAYagMeiWXwAAAANzQklUBAQEd/i1owAAAAlwSFlzAAAABAAAAAEAH+hVZQAAAOtJREFUeJx9kmuxwyAUhL/M1MBawEIsxEIsxEIs3FiohVjAAhaOhSMh9wePQtt0hwEG5rx2d7r4QADVxbg3eN3zxbr7iEc5BTOssJaHFtIHeleo9RDao8EBCawvIFgglN5dRIjwLLNsuHBh3RRy5AQgwSn8D2aYA+TlEEuZB+sr0EpeHJE/zl1PtshGrMPICAdz3GFNzIJoJANr8+foE6xRdAeBMJGQqhSGnE6kOzjAdPUULfjyRtECAQfXIEJmCYMY8D1T5HDU1JWi6WqdhkFkH63xZhCNIr8oPsAGkacvNu2d8YOH3ql+a9N/CM1cqmi++6QAAAAASUVORK5CYII=",
3083       "/wAA//8AAP//AAD//wAA//8AAP//AAD//yIA//8zAP//RAD//1UA//9VAP//ZgD//2YA//93AP//dwD//3cA//93AP//dwD//2YA//9mAP//VQD//0QA//9EAP//IgD//xEA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8RAP//RAD//5kA///uAP//7gD//+4A/93uAP/M7gD/u+4A/6ruAP+Z7gD/me4A/5nuAP+I7gD/me4A/5nuAP+Z7gD/qu4A/7vuAP/M7gD/7u4A///uAP//7gD//8wA//9VAP//IgD//wAA//8AAP//AAD//wAA//8RAP//VQD//3cA//93AP//uwD/7v8A/6r/AP9m/wD/AP8A/wD/Iv8A/4j/AO6Z/wDdqv8Au6r/ALu7/wC7u/8Au7v/AMyq/wDdmf8A/5n/AP9m/wD/Ef8R/wD/mf8A/8z/AP//7gD//3cA//93AP//ZgD//yIA//8AAP//AAD//2YA//93AP//dwD//3cA/927AP93/wD/d/8A/1X/Ef8A/2b/AP93/wCq//8RRP//dwD//90Au//dAKr/3QCZ/90Aqv+7AMz/RAD//wB3//8AzMz/AP93/xH/M/93/wD/d/8A/6ruAP//dwD//3cA//93AP//dwD//yIA//8AAP//ZgD//3cA//93AP//dwD/3bsA/3f/AP93/wD/Vf8R/wD/Zv8A/3f/ALv//xFV//9VEf//qhHM/7sRu/+7Ebv/uxG7/5kR3f8zEf//AIj//wDdzP8A/3f/Ef8z/3f/AP93/wD/qu4A//93AP//dwD//3cA//93AP//EQD//wAA//8RAP//VQD//3cA//93AP//uwD/7v8A/6r/AP9m/wD/AP8A/wD/Iv8A/2b/AP+I/wDuiP8A3Zn/AN2Z/wDMmf8A3Zn/AN2Z/wDuiP8A/3f/AP9V/wD/Ef8i/wD/mf8A/8z/AP//7gD//3cA//93AP//ZgD//yIA//8AAP//AAD//wAA//8AAP//EQD//zMA//+IAP//3QD//90A///dAP/u3QD/zN0A/7vdAP+q3QD/qt0A/5ndAP+Z3QD/md0A/5ndAP+Z3QD/qt0A/7vdAP/M3QD/3d0A///dAP//3QD//90A//+7AP//RAD//yIA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//EQD//zMA//9EAP//RAD//1UA//9mAP//ZgD//2YA//9mAP//ZgD//2YA//9mAP//ZgD//1UA//9VAP//RAD//zMA//8iAP//EQD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA/w==");
3084   testPNGSuiteImage("cdsn2c08.png", false,
3085       "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAABGdBTUEAAYagMeiWXwAAAANzQklUBAQEd/i1owAAAAlwSFlzAAAAAQAAAAEATyXE1gAAAHtJREFUeJxFzlENwkAURNFTgoFnYS3UQisBC1gACSCBSsDCVgIroSuhlbB8NIX5mUwyubldQzCQ2KjMcBZ8zKFiP0zcaRd53Stb3n2LNWvJSVIwX/PoNvbbNlQkJ0dqRI0Qxz5Qg+VlffxQXQuyEsphFxNP3V93hxQKfAEqsC/QF17WrgAAAABJRU5ErkJggg==",
3086       "/wAA//8RAP//VQD//3cA//9mAP//RAD//wAA//8AAP//EQD//90A/7vuAP+Z7gD/me4A/93uAP//iAD//wAA//9VAP+77gD/AP9V/wC7u/8A3ar/M/8R/+67AP//IgD//3cA/4juAP8Au7v/uwC7/3ci3f8i7lX/zLsA//9EAP//dwD/iO4A/wC7u/+ZEcz/VTPu/yL/Vf/MuwD//0QA//9VAP+77gD/AP9E/wDdmf8A7oj/M/8R/+67AP//IgD//xEA///MAP/M3QD/md0A/6rdAP/u3QD//3cA//8AAP//AAD//xEA//9VAP//ZgD//2YA//8zAP//AAD//wAA/w==");
3087   testPNGSuiteImage("s01i3p01.png", false,
3088       "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAFS3GZcAAAABGdBTUEAAYagMeiWXwAAAANzQklUBAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=",
3089       "AAD//w==");
3090   testPNGSuiteImage("s01n3p01.png", false,
3091       "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABGdBTUEAAYagMeiWXwAAAANzQklUBAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=",
3092       "AAD//w==");
3093   testPNGSuiteImage("s02i3p01.png", false,
3094       "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAQMAAAE/f6/xAAAABGdBTUEAAYagMeiWXwAAAANzQklUBAQEd/i1owAAAANQTFRFAP//GVwvJQAAAAtJREFUeJxjYAABAAAGAAH+jGfIAAAAAElFTkSuQmCC",
3095       "AP///wD///8A////AP///w==");
3096   testPNGSuiteImage("s02n3p01.png", false,
3097       "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAQMAAABIeJ9nAAAABGdBTUEAAYagMeiWXwAAAANzQklUBAQEd/i1owAAAANQTFRFAP//GVwvJQAAAAxJREFUeJxjYGBgAAAABAAB9hc4VQAAAABJRU5ErkJggg==",
3098       "AP///wD///8A////AP///w==");
3099   testPNGSuiteImage("xd0n2c08.png", true,
3100       "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAAIAAADMaKZiAAAABGdBTUEAAYagMeiWXwAAAEhJREFUeJzt1cEJADAMAkCF7JH9t3ITO0Qr9KH4zuErtA0EO4AKFPgcoO3kfUx4QIECD0qHH8KEBxQo8KB0OCOpQIG7cHejwAGCsfleD0DPSwAAAABJRU5ErkJggg==",
3101       "");
3102   testPNGSuiteImage("xd3n2c08.png", true,
3103       "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAwIAAACLyNyyAAAABGdBTUEAAYagMeiWXwAAAEhJREFUeJzt1cEJADAMAkCF7JH9t3ITO0Qr9KH4zuErtA0EO4AKFPgcoO3kfUx4QIECD0qHH8KEBxQo8KB0OCOpQIG7cHejwAGCsfleD0DPSwAAAABJRU5ErkJggg==",
3104       "");
3105   testPNGSuiteImage("xd9n2c08.png", true,
3106       "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgYwIAAAAS+qv/AAAABGdBTUEAAYagMeiWXwAAAEhJREFUeJzt1cEJADAMAkCF7JH9t3ITO0Qr9KH4zuErtA0EO4AKFPgcoO3kfUx4QIECD0qHH8KEBxQo8KB0OCOpQIG7cHejwAGCsfleD0DPSwAAAABJRU5ErkJggg==",
3107       "");
3108   testPNGSuiteImage("xdtn0g01.png", true,
3109       "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQAAAABbAUdZAAAABGdBTUEAAYagMeiWXwAAAABJRU5ErkJggg==",
3110       "");
3111   testPNGSuiteImage("xhdn0g08.png", true,
3112       "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAAAAABDU1VNAAAABGdBTUEAAYagMeiWXwAAAEFJREFUeJxjZGAkABQIyLMMBQWMDwgp+PcfP2B5MBwUMMoRkGdkonlcDAYFjI/wyv7/z/iH5nExGBQwyuCVZWQEAFDl/nE14thZAAAAAElFTkSuQmCC",
3113       "");
3114   testPNGSuiteImage("xlfn0g04.png", true,
3115       "iVBORwoKGgoAAAAKSUhEUgAAACAAAAAgBAAAAACT4cgpAAAABGdBTUEAAYagMeiWXwAAAEhJREFUeJxjYGAQFFRSMjZ2cQkKTUsrL2cgQwCV29FBjgAqd+ZMcgRQuatWkSOAyt29mxwBVO6ZM+QIoHLv3iVHAJX77h0ZAgAfFO4B6v9B+gAAAABJRU5ErkJggg==",
3116       "");
3117   testPNGSuiteImage("xs1n0g01.png", true,
3118       "CVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQAAAABbAUdZAAAABGdBTUEAAYagMeiWXwAAAFtJREFUeJwtzLEJAzAMBdHr0gSySiALejRvkBU8gsGNCmFFB1Hx4IovqurSpIRszqklUwbnUzRXEuIRsiG/SyY9G0JzJSVei9qynm9qyjBpLp0pYW7pbzBl8L8fEIdJL9AvFMkAAAAASUVORK5CYII=",
3119       "");
3120   testPNGSuiteImage("xs2n0g01.png", true,
3121       "iVFORw0KGgoAAAANSUhEUgAAACAAAAAgAQAAAABbAUdZAAAABGdBTUEAAYagMeiWXwAAAFtJREFUeJwtzLEJAzAMBdHr0gSySiALejRvkBU8gsGNCmFFB1Hx4IovqurSpIRszqklUwbnUzRXEuIRsiG/SyY9G0JzJSVei9qynm9qyjBpLp0pYW7pbzBl8L8fEIdJL9AvFMkAAAAASUVORK5CYII=",
3122       "");
3123   testPNGSuiteImage("xs4n0g01.png", true,
3124       "iVBOZw0KGgoAAAANSUhEUgAAACAAAAAgAQAAAABbAUdZAAAABGdBTUEAAYagMeiWXwAAAFtJREFUeJwtzLEJAzAMBdHr0gSySiALejRvkBU8gsGNCmFFB1Hx4IovqurSpIRszqklUwbnUzRXEuIRsiG/SyY9G0JzJSVei9qynm9qyjBpLp0pYW7pbzBl8L8fEIdJL9AvFMkAAAAASUVORK5CYII=",
3125       "");
3126   testPNGSuiteImage("xs7n0g01.png", true,
3127       "iVBORw0KIAoAAAANSUhEUgAAACAAAAAgAQAAAABbAUdZAAAABGdBTUEAAYagMeiWXwAAAFtJREFUeJwtzLEJAzAMBdHr0gSySiALejRvkBU8gsGNCmFFB1Hx4IovqurSpIRszqklUwbnUzRXEuIRsiG/SyY9G0JzJSVei9qynm9qyjBpLp0pYW7pbzBl8L8fEIdJL9AvFMkAAAAASUVORK5CYII=",
3128       "");
3129   /*testPNGSuiteImage("name.png", false,
3130       "png64",
3131       "pixels64");
3132   testPNGSuiteImage("error.png", true,
3133       "png64",
3134       "");*/
3135 }
3136 
doMain()3137 void doMain() {
3138   //PNG
3139   testPNGSuite();
3140   testPNGCodec();
3141   testPngSuiteTiny();
3142   testPaletteFilterTypesZero();
3143   testComplexPNG();
3144   testInspectChunk();
3145   testPredefinedFilters();
3146   testFuzzing();
3147   testEncoderErrors();
3148   testPaletteToPaletteDecode();
3149   testPaletteToPaletteDecode2();
3150   testColorProfile();
3151   testBkgdChunk();
3152   testBkgdChunk2();
3153 
3154   //Colors
3155 #ifndef DISABLE_SLOW
3156   testFewColors();
3157 #endif // DISABLE_SLOW
3158   testColorKeyConvert();
3159   testColorConvert();
3160   testColorConvert2();
3161   testPaletteToPaletteConvert();
3162   testRGBToPaletteConvert();
3163   test16bitColorEndianness();
3164   testAutoColorModels();
3165   testNoAutoConvert();
3166   testChrmToSrgb();
3167   testXYZ();
3168   testICC();
3169   testICCGray();
3170 
3171   //Zlib
3172   testCompressZlib();
3173   testHuffmanCodeLengths();
3174   testCustomZlibCompress();
3175   testCustomZlibCompress2();
3176   testCustomDeflate();
3177   testCustomZlibDecompress();
3178   testCustomInflate();
3179 
3180   //lodepng_util
3181   testChunkUtil();
3182 
3183   std::cout << "\ntest successful" << std::endl;
3184 }
3185 
main()3186 int main() {
3187   try {
3188     doMain();
3189   }
3190   catch(...) {
3191     std::cout << "error!" << std::endl;
3192   }
3193 
3194   return 0;
3195 }
3196