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