-
Notifications
You must be signed in to change notification settings - Fork 1
/
lodepng_unittest.cpp
2691 lines (2341 loc) · 98.6 KB
/
lodepng_unittest.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
LodePNG Unit Test
Copyright (c) 2005-2018 Lode Vandevenne
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
//g++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wsign-conversion -pedantic -ansi -O3
/*
Testing instructions:
*) Ensure no tests commented out below or early return in doMain
*) Compile with g++ or clang++ with all warnings and run the unit test
g++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wsign-conversion -Wshadow -pedantic -ansi -O3 && ./a.out
clang++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wsign-conversion -Wshadow -pedantic -ansi -O3 && ./a.out
*) Compile with pure ISO C90 and all warnings:
mv lodepng.cpp lodepng.c ; gcc -I ./ lodepng.c examples/example_decode.c -ansi -pedantic -Wall -Wextra -O3 ; mv lodepng.c lodepng.cpp
*) Compile with C with -pedantic but not -ansi flag so it warns about // style comments in C++-only ifdefs
mv lodepng.cpp lodepng.c ; gcc -I ./ lodepng.c examples/example_decode.c -pedantic -Wall -Wextra -O3 ; mv lodepng.c lodepng.cpp
*) try lodepng_benchmark.cpp
g++ lodepng.cpp lodepng_benchmark.cpp -Wall -Wextra -pedantic -ansi -lSDL -O3 && ./a.out
g++ lodepng.cpp lodepng_benchmark.cpp -Wall -Wextra -pedantic -ansi -lSDL -O3 && ./a.out corpus/''*
*) Check if all examples compile without warnings:
g++ -I ./ lodepng.cpp examples/''*.cpp -W -Wall -ansi -pedantic -O3 -c
mv lodepng.cpp lodepng.c ; gcc -I ./ lodepng.c examples/''*.c -W -Wall -ansi -pedantic -O3 -c ; mv lodepng.c lodepng.cpp
*) Check pngdetail.cpp:
g++ lodepng.cpp lodepng_util.cpp pngdetail.cpp -W -Wall -ansi -pedantic -O3 -o pngdetail
./pngdetail testdata/PngSuite/basi0g01.png
*) Test compiling with some code sections with #defines disabled, for unused static function warnings etc...
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ZLIB
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_PNG
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_DECODER
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ENCODER
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_DISK
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ERROR_TEXT
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_CPP
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ZLIB -DLODEPNG_NO_COMPILE_DECODER
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ZLIB -DLODEPNG_NO_COMPILE_ENCODER
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_PNG -DLODEPNG_NO_COMPILE_DECODER
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_PNG -DLODEPNG_NO_COMPILE_ENCODER
rm *.o
*) analyze with clang:
clang++ lodepng.cpp --analyze
More verbose:
clang++ --analyze -Xanalyzer -analyzer-output=text lodepng.cpp
Or html, look under lodepng.plist dir afterwards and find the numbered locations in the pages:
clang++ --analyze -Xanalyzer -analyzer-output=html lodepng.cpp
*) check for memory leaks and vulnerabilities with valgrind
(DISABLE_SLOW disables a few tests that are very slow with valgrind)
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
*) Try with clang++ and address sanitizer (to get line numbers, make sure 'llvm' is also installed to get 'llvm-symbolizer'
clang++ -fsanitize=address lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wshadow -pedantic -ansi -O3 && ASAN_OPTIONS=allocator_may_return_null=1 ./a.out
clang++ -fsanitize=address lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wshadow -pedantic -ansi -g3 && ASAN_OPTIONS=allocator_may_return_null=1 ./a.out
*) remove "#include <iostream>" from lodepng.cpp if it's still in there
cat lodepng.cpp | grep iostream
cat lodepng.cpp | grep "#include"
*) check that no plain "free", "malloc" and "realloc" used, but the lodepng_* versions instead
*) check version dates in copyright message and LODEPNG_VERSION_STRING
*) check year in copyright message at top of all files as well as at bottom of lodepng.h
*) check examples/sdl.cpp with the png test suite images (the "x" ones are expected to show error)
g++ -I ./ lodepng.cpp examples/example_sdl.cpp -Wall -Wextra -pedantic -ansi -O3 -lSDL -o showpng && ./showpng testdata/PngSuite/''*.png
*) strip trailing spaces and ensure consistent newlines
*) check diff of lodepng.cpp and lodepng.h before submitting
git difftool -y
*/
#include "lodepng.h"
#include "lodepng_util.h"
#include <cmath>
#include <map>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
////////////////////////////////////////////////////////////////////////////////
void fail()
{
throw 1; //that's how to let a unittest fail
}
template<typename T, typename U>
void assertEquals(const T& expected, const U& actual, const std::string& message = "")
{
if(expected != (T)actual)
{
std::cout << "Error: Not equal! Expected " << expected << " got " << (T)actual << ". "
<< "Message: " << message << std::endl;
fail();
}
}
template<typename T, typename U>
void assertNotEquals(const T& expected, const U& actual, const std::string& message = "")
{
if(expected == (T)actual)
{
std::cout << "Error: Equal but expected not equal! Expected not " << expected << " got " << (T)actual << ". "
<< "Message: " << message << std::endl;
fail();
}
}
void assertTrue(bool value, const std::string& message = "")
{
if(!value)
{
std::cout << "Error: expected true. " << "Message: " << message << std::endl;
fail();
}
}
//assert that no error
void assertNoPNGError(unsigned error, const std::string& message = "")
{
if(error)
{
std::string msg = (message == "") ? lodepng_error_text(error)
: message + std::string(": ") + lodepng_error_text(error);
assertEquals(0, error, msg);
}
}
void assertNoError(unsigned error)
{
if(error)
{
assertEquals(0, error, "Expected no error");
}
}
#define STR_EXPAND(s) #s
#define STR(s) STR_EXPAND(s)
#define ASSERT_EQUALS(e, v) \
{\
assertEquals(e, v, std::string() + "line " + STR(__LINE__) + ": " + STR(v) + " ASSERT_EQUALS(" + #e + ", " + #v + ")");\
}
#define ASSERT_NOT_EQUALS(e, v) \
{\
assertNotEquals(e, v, std::string() + "line " + STR(__LINE__) + ": " + STR(v) + " ASSERT_NOT_EQUALS(" + #e + ", " + #v + ")");\
}
#define ASSERT_STRING_EQUALS(e, v) ASSERT_EQUALS(std::string(e), std::string(v))
#define ASSERT_NO_PNG_ERROR_MSG(error, message) assertNoPNGError(error, std::string("line ") + STR(__LINE__) + (std::string(message).empty() ? std::string("") : (": " + std::string(message))))
#define ASSERT_NO_PNG_ERROR(error) ASSERT_NO_PNG_ERROR_MSG(error, std::string(""))
static const std::string BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//T and U can be std::string or std::vector<unsigned char>
template<typename T, typename U>
void toBase64(T& out, const U& in)
{
for(size_t i = 0; i < in.size(); i += 3)
{
int v = 65536 * in[i];
if(i + 1 < in.size()) v += 256 * in[i + 1];
if(i + 2 < in.size()) v += in[i + 2];
out.push_back(BASE64[(v >> 18) & 0x3f]);
out.push_back(BASE64[(v >> 12) & 0x3f]);
if(i + 1 < in.size()) out.push_back(BASE64[(v >> 6) & 0x3f]);
else out.push_back('=');
if(i + 2 < in.size()) out.push_back(BASE64[(v >> 0) & 0x3f]);
else out.push_back('=');
}
}
int fromBase64(int v)
{
if(v >= 'A' && v <= 'Z') return (v - 'A');
if(v >= 'a' && v <= 'z') return (v - 'a' + 26);
if(v >= '0' && v <= '9') return (v - '0' + 52);
if(v == '+') return 62;
if(v == '/') return 63;
return 0; //v == '='
}
//T and U can be std::string or std::vector<unsigned char>
template<typename T, typename U>
void fromBase64(T& out, const U& in)
{
for(size_t i = 0; i + 3 < in.size(); i += 4)
{
int v = 262144 * fromBase64(in[i]) + 4096 * fromBase64(in[i + 1]) + 64 * fromBase64(in[i + 2]) + fromBase64(in[i + 3]);
out.push_back((v >> 16) & 0xff);
if(in[i + 2] != '=') out.push_back((v >> 8) & 0xff);
if(in[i + 3] != '=') out.push_back((v >> 0) & 0xff);
}
}
////////////////////////////////////////////////////////////////////////////////
//Test image data
struct Image
{
std::vector<unsigned char> data;
unsigned width;
unsigned height;
LodePNGColorType colorType;
unsigned bitDepth;
};
//Utility for debug messages
template<typename T>
std::string valtostr(const T& val)
{
std::ostringstream sstream;
sstream << val;
return sstream.str();
}
//Get number of color channels for a given PNG color type
unsigned getNumColorChannels(unsigned colorType)
{
switch(colorType)
{
case 0: return 1; /*grey*/
case 2: return 3; /*RGB*/
case 3: return 1; /*palette*/
case 4: return 2; /*grey + alpha*/
case 6: return 4; /*RGBA*/
}
return 0; /*unexisting color type*/
}
//Generate a test image with some data in it, the contents of the data is unspecified,
//except the content is not just one plain color, and not true random either to be compressible.
void generateTestImage(Image& image, unsigned width, unsigned height, LodePNGColorType colorType = LCT_RGBA, unsigned bitDepth = 8)
{
image.width = width;
image.height = height;
image.colorType = colorType;
image.bitDepth = bitDepth;
size_t bits = bitDepth * getNumColorChannels(colorType); //bits per pixel
size_t size = (width * height * bits + 7) / 8; //total image size in bytes
image.data.resize(size);
unsigned char value = 128;
for(size_t i = 0; i < size; i++)
{
image.data[i] = value++;
}
}
//Generate a 16-bit test image with minimal size that requires at minimum the given color type (bit depth, greyscaleness, ...)
//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
//whether it's an alpha color type or not.
void generateTestImageRequiringColorType16(Image& image, LodePNGColorType colorType, unsigned bitDepth, bool key)
{
image.colorType = colorType;
image.bitDepth = bitDepth;
unsigned w = 1;
unsigned h = 1;
bool grey = colorType == LCT_GREY || colorType == LCT_GREY_ALPHA;
bool alpha = colorType == LCT_RGBA || colorType == LCT_GREY_ALPHA;
if(colorType == LCT_PALETTE)
{
w = 1u << bitDepth;
h = 256; // ensure it'll really choose palette, not omit it due to small image size
image.data.resize(w * h * 8);
for(size_t y = 0; y < h; y++)
{
for(size_t x = 0; x < w; x++)
{
size_t i = y * w * 8 + x * 8;
image.data[i + 0] = image.data[i + 1] = y;
image.data[i + 2] = image.data[i + 3] = 255;
image.data[i + 4] = image.data[i + 5] = 0;
image.data[i + 6] = image.data[i + 7] = (key && y == 0) ? 0 : 255;
}
}
}
else if(bitDepth == 16)
{
// one color suffices for this model. But add one more to support key.
w = 2;
image.data.resize(w * h * 8);
image.data[0] = 10; image.data[1] = 20;
image.data[2] = 10; image.data[3] = 20;
image.data[4] = grey ? 10 : 110; image.data[5] = grey ? 20 : 120;
image.data[6] = alpha ? 128 : 255; image.data[7] = alpha ? 20 : 255;
image.data[8] = 40; image.data[9] = 50;
image.data[10] = 40; image.data[11] = 50;
image.data[12] = grey ? 40 : 140; image.data[13] = grey ? 50 : 150;
image.data[14] = key ? 0 : 255; image.data[15] = key ? 0 : 255;
}
else if(grey)
{
w = 2;
unsigned v = 255u / ((1u << bitDepth) - 1u); // value that forces at least this bitdepth
image.data.resize(w * h * 8);
image.data[0] = v; image.data[1] = v;
image.data[2] = v; image.data[3] = v;
image.data[4] = v; image.data[5] = v;
image.data[6] = alpha ? v : 255; image.data[7] = alpha ? v : 255;
image.data[8] = image.data[9] = 0;
image.data[10] = image.data[11] = 0;
image.data[12] = image.data[13] = 0;
image.data[14] = image.data[15] = key ? 0 : 255;
}
else
{
// now it's RGB or RGBA with bitdepth 8
w = 257; // must have at least more than 256 colors so it won't use palette
image.data.resize(w * h * 8);
for(size_t y = 0; y < h; y++)
{
for(size_t x = 0; x < w; x++)
{
size_t i = y * w * 8 + x * 8;
image.data[i + 0] = image.data[i + 1] = i / 2;
image.data[i + 2] = image.data[i + 3] = i / 3;
image.data[i + 4] = image.data[i + 5] = i / 5;
image.data[i + 6] = image.data[i + 7] = (key && y == 0) ? 0 : (alpha ? i : 255);
}
}
}
image.width = w;
image.height = h;
}
//Generate a 8-bit test image with minimal size that requires at minimum the given color type (bit depth, greyscaleness, ...). bitDepth max 8 here.
//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
//whether it's an alpha color type or not.
void generateTestImageRequiringColorType8(Image& image, LodePNGColorType colorType, unsigned bitDepth, bool key)
{
image.colorType = colorType;
image.bitDepth = bitDepth;
unsigned w = 1;
unsigned h = 1;
bool grey = colorType == LCT_GREY || colorType == LCT_GREY_ALPHA;
bool alpha = colorType == LCT_RGBA || colorType == LCT_GREY_ALPHA;
if(colorType == LCT_PALETTE)
{
w = 1u << bitDepth;
h = 256; // ensure it'll really choose palette, not omit it due to small image size
image.data.resize(w * h * 4);
for(size_t y = 0; y < h; y++)
{
for(size_t x = 0; x < w; x++)
{
size_t i = y * w * 4 + x * 4;
image.data[i + 0] = x;
image.data[i + 1] = 255;
image.data[i + 2] = 0;
image.data[i + 3] = (key && x == 0) ? 0 : 255;
}
}
}
else if(grey)
{
w = 2;
unsigned v = 255u / ((1u << bitDepth) - 1u); // value that forces at least this bitdepth
image.data.resize(w * h * 4);
image.data[0] = v;
image.data[1] = v;
image.data[2] = v;
image.data[3] = alpha ? v : 255;
image.data[4] = 0;
image.data[5] = 0;
image.data[6] = 0;
image.data[7] = key ? 0 : 255;
}
else
{
// now it's RGB or RGBA with bitdepth 8
w = 257; // must have at least more than 256 colors so it won't use palette
image.data.resize(w * h * 4);
for(size_t y = 0; y < h; y++)
{
for(size_t x = 0; x < w; x++)
{
size_t i = y * w * 4 + x * 4;
image.data[i + 0] = i / 2;
image.data[i + 1] = i / 3;
image.data[i + 2] = i / 5;
image.data[i + 3] = (key && x == 0) ? 0 : (alpha ? i : 255);
}
}
}
image.width = w;
image.height = h;
}
//Check that the decoded PNG pixels are the same as the pixels in the image
void assertPixels(Image& image, const unsigned char* decoded, const std::string& message)
{
for(size_t i = 0; i < image.data.size(); i++)
{
int byte_expected = image.data[i];
int byte_actual = decoded[i];
//last byte is special due to possible random padding bits which need not to be equal
if(i == image.data.size() - 1)
{
size_t numbits = getNumColorChannels(image.colorType) * image.bitDepth * image.width * image.height;
size_t padding = 8u - (numbits - 8u * (numbits / 8u));
if(padding != 8u)
{
//set all padding bits of both to 0
for(size_t j = 0; j < padding; j++)
{
byte_expected = (byte_expected & (~(1 << j))) % 256;
byte_actual = (byte_actual & (~(1 << j))) % 256;
}
}
}
assertEquals(byte_expected, byte_actual, message + " " + valtostr(i));
}
}
//Test LodePNG encoding and decoding the encoded result, using the C interface
void doCodecTestC(Image& image)
{
unsigned char* encoded = 0;
size_t encoded_size = 0;
unsigned char* decoded = 0;
unsigned decoded_w;
unsigned decoded_h;
struct OnExitScope
{
unsigned char** a;
unsigned char** b;
OnExitScope(unsigned char** ca, unsigned char** cb) : a(ca), b(cb) {}
~OnExitScope() { free(*a); free(*b); }
} onExitScope(&encoded, &decoded);
unsigned error_enc = lodepng_encode_memory(&encoded, &encoded_size, &image.data[0],
image.width, image.height, image.colorType, image.bitDepth);
if(error_enc != 0) std::cout << "Error: " << lodepng_error_text(error_enc) << std::endl;
ASSERT_NO_PNG_ERROR_MSG(error_enc, "encoder error C");
//if the image is large enough, compressing it should result in smaller size
if(image.data.size() > 512) assertTrue(encoded_size < image.data.size(), "compressed size");
unsigned error_dec = lodepng_decode_memory(&decoded, &decoded_w, &decoded_h,
encoded, encoded_size, image.colorType, image.bitDepth);
if(error_dec != 0) std::cout << "Error: " << lodepng_error_text(error_dec) << std::endl;
ASSERT_NO_PNG_ERROR_MSG(error_dec, "decoder error C");
ASSERT_EQUALS(image.width, decoded_w);
ASSERT_EQUALS(image.height, decoded_h);
assertPixels(image, decoded, "Pixels C");
}
//Test LodePNG encoding and decoding the encoded result, using the C++ interface
void doCodecTestCPP(Image& image)
{
std::vector<unsigned char> encoded;
std::vector<unsigned char> decoded;
unsigned decoded_w;
unsigned decoded_h;
unsigned error_enc = lodepng::encode(encoded, image.data, image.width, image.height,
image.colorType, image.bitDepth);
ASSERT_NO_PNG_ERROR_MSG(error_enc, "encoder error C++");
//if the image is large enough, compressing it should result in smaller size
if(image.data.size() > 512) assertTrue(encoded.size() < image.data.size(), "compressed size");
unsigned error_dec = lodepng::decode(decoded, decoded_w, decoded_h, encoded, image.colorType, image.bitDepth);
ASSERT_NO_PNG_ERROR_MSG(error_dec, "decoder error C++");
ASSERT_EQUALS(image.width, decoded_w);
ASSERT_EQUALS(image.height, decoded_h);
ASSERT_EQUALS(image.data.size(), decoded.size());
assertPixels(image, &decoded[0], "Pixels C++");
}
void doCodecTestWithEncState(Image& image, lodepng::State& state) {
std::vector<unsigned char> encoded;
std::vector<unsigned char> decoded;
unsigned decoded_w;
unsigned decoded_h;
state.info_raw.colortype = image.colorType;
state.info_raw.bitdepth = image.bitDepth;
unsigned error_enc = lodepng::encode(encoded, image.data, image.width, image.height, state);
ASSERT_NO_PNG_ERROR_MSG(error_enc, "encoder error uncompressed");
unsigned error_dec = lodepng::decode(decoded, decoded_w, decoded_h, encoded, image.colorType, image.bitDepth);
ASSERT_NO_PNG_ERROR_MSG(error_dec, "decoder error uncompressed");
ASSERT_EQUALS(image.width, decoded_w);
ASSERT_EQUALS(image.height, decoded_h);
ASSERT_EQUALS(image.data.size(), decoded.size());
assertPixels(image, &decoded[0], "Pixels uncompressed");
}
//Test LodePNG encoding and decoding the encoded result, using the C++ interface
void doCodecTestUncompressed(Image& image)
{
lodepng::State state;
state.encoder.zlibsettings.btype = 0;
doCodecTestWithEncState(image, state);
}
void doCodecTestNoLZ77(Image& image)
{
lodepng::State state;
state.encoder.zlibsettings.use_lz77 = 0;
doCodecTestWithEncState(image, state);
}
//Test LodePNG encoding and decoding the encoded result, using the C++ interface, with interlace
void doCodecTestInterlaced(Image& image)
{
std::vector<unsigned char> encoded;
std::vector<unsigned char> decoded;
unsigned decoded_w;
unsigned decoded_h;
lodepng::State state;
state.info_png.interlace_method = 1;
state.info_raw.colortype = image.colorType;
state.info_raw.bitdepth = image.bitDepth;
unsigned error_enc = lodepng::encode(encoded, image.data, image.width, image.height, state);
ASSERT_NO_PNG_ERROR_MSG(error_enc, "encoder error interlaced");
//if the image is large enough, compressing it should result in smaller size
if(image.data.size() > 512) assertTrue(encoded.size() < image.data.size(), "compressed size");
state.info_raw.colortype = image.colorType;
state.info_raw.bitdepth = image.bitDepth;
unsigned error_dec = lodepng::decode(decoded, decoded_w, decoded_h, state, encoded);
ASSERT_NO_PNG_ERROR_MSG(error_dec, "decoder error interlaced");
ASSERT_EQUALS(image.width, decoded_w);
ASSERT_EQUALS(image.height, decoded_h);
ASSERT_EQUALS(image.data.size(), decoded.size());
assertPixels(image, &decoded[0], "Pixels interlaced");
}
//Test LodePNG encoding and decoding the encoded result
void doCodecTest(Image& image)
{
doCodecTestC(image);
doCodecTestCPP(image);
doCodecTestInterlaced(image);
doCodecTestUncompressed(image);
doCodecTestNoLZ77(image);
}
//Test LodePNG encoding and decoding using some image generated with the given parameters
void codecTest(unsigned width, unsigned height, LodePNGColorType colorType = LCT_RGBA, unsigned bitDepth = 8)
{
std::cout << "codec test " << width << " " << height << std::endl;
Image image;
generateTestImage(image, width, height, colorType, bitDepth);
doCodecTest(image);
}
std::string removeSpaces(const std::string& s)
{
std::string result;
for(size_t i = 0; i < s.size(); i++) if(s[i] != ' ') result += s[i];
return result;
}
void bitStringToBytes(std::vector<unsigned char>& bytes, const std::string& bits_)
{
std::string bits = removeSpaces(bits_);
bytes.resize((bits.size()) + 7 / 8);
for(size_t i = 0; i < bits.size(); i++)
{
size_t j = i / 8;
size_t k = i % 8;
char c = bits[i];
if(k == 0) bytes[j] = 0;
if(c == '1') bytes[j] |= (1 << (7 - k));
}
}
/*
test color convert on a single pixel. Testing palette and testing color keys is
not supported by this function. Pixel values given using bits in an std::string
of 0's and 1's.
*/
void colorConvertTest(const std::string& bits_in, LodePNGColorType colorType_in, unsigned bitDepth_in,
const std::string& bits_out, LodePNGColorType colorType_out, unsigned bitDepth_out)
{
std::cout << "color convert test " << bits_in << " - " << bits_out << std::endl;
std::vector<unsigned char> expected, actual, image;
bitStringToBytes(expected, bits_out);
actual.resize(expected.size());
bitStringToBytes(image, bits_in);
LodePNGColorMode mode_in, mode_out;
lodepng_color_mode_init(&mode_in);
lodepng_color_mode_init(&mode_out);
mode_in.colortype = colorType_in;
mode_in.bitdepth = bitDepth_in;
mode_out.colortype = colorType_out;
mode_out.bitdepth = bitDepth_out;
unsigned error = lodepng_convert(&actual[0], &image[0], &mode_out, &mode_in, 1, 1);
ASSERT_NO_PNG_ERROR_MSG(error, "convert error");
for(size_t i = 0; i < expected.size(); i++)
{
assertEquals((int)expected[i], (int)actual[i], "byte " + valtostr(i));
}
lodepng_color_mode_cleanup(&mode_in);
lodepng_color_mode_cleanup(&mode_out);
}
void testOtherPattern1()
{
std::cout << "codec other pattern 1" << std::endl;
Image image1;
size_t w = 192;
size_t h = 192;
image1.width = w;
image1.height = h;
image1.colorType = LCT_RGBA;
image1.bitDepth = 8;
image1.data.resize(w * h * 4u);
for(size_t y = 0; y < h; y++)
for(size_t x = 0; x < w; x++)
{
//pattern 1
image1.data[4u * w * y + 4u * x + 0u] = (unsigned char)(127 * (1 + std::sin(( x * x + y * y) / (w * h / 8.0))));
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))));
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))));
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))));
}
doCodecTest(image1);
}
void testOtherPattern2()
{
std::cout << "codec other pattern 2" << std::endl;
Image image1;
size_t w = 192;
size_t h = 192;
image1.width = w;
image1.height = h;
image1.colorType = LCT_RGBA;
image1.bitDepth = 8;
image1.data.resize(w * h * 4u);
for(size_t y = 0; y < h; y++)
for(size_t x = 0; x < w; x++)
{
image1.data[4u * w * y + 4u * x + 0u] = 255 * !(x & y);
image1.data[4u * w * y + 4u * x + 1u] = x ^ y;
image1.data[4u * w * y + 4u * x + 2u] = x | y;
image1.data[4u * w * y + 4u * x + 3u] = 255;
}
doCodecTest(image1);
}
void testSinglePixel(int r, int g, int b, int a)
{
std::cout << "codec single pixel " << r << " " << g << " " << b << " " << a << std::endl;
Image pixel;
pixel.width = 1;
pixel.height = 1;
pixel.colorType = LCT_RGBA;
pixel.bitDepth = 8;
pixel.data.resize(4);
pixel.data[0] = r;
pixel.data[1] = g;
pixel.data[2] = b;
pixel.data[3] = a;
doCodecTest(pixel);
}
void testColor(int r, int g, int b, int a)
{
std::cout << "codec test color " << r << " " << g << " " << b << " " << a << std::endl;
Image image;
image.width = 20;
image.height = 20;
image.colorType = LCT_RGBA;
image.bitDepth = 8;
image.data.resize(20 * 20 * 4);
for(size_t y = 0; y < 20; y++)
for(size_t x = 0; x < 20; x++)
{
image.data[20 * 4 * y + 4 * x + 0] = r;
image.data[20 * 4 * y + 4 * x + 0] = g;
image.data[20 * 4 * y + 4 * x + 0] = b;
image.data[20 * 4 * y + 4 * x + 0] = a;
}
doCodecTest(image);
Image image2 = image;
image2.data[3] = 0; //one fully transparent pixel
doCodecTest(image2);
image2.data[3] = 128; //one semi transparent pixel
doCodecTest(image2);
Image image3 = image;
// add 255 different colors
for(size_t i = 0; i < 255; i++) {
image.data[i * 4 + 0] = i;
image.data[i * 4 + 1] = i;
image.data[i * 4 + 2] = i;
image.data[i * 4 + 3] = 255;
}
doCodecTest(image3);
// a 256th color
image.data[255 * 4 + 0] = 255;
image.data[255 * 4 + 1] = 255;
image.data[255 * 4 + 2] = 255;
image.data[255 * 4 + 3] = 255;
doCodecTest(image3);
testSinglePixel(r, g, b, a);
}
// Tests combinations of various colors in different orders
void testFewColors()
{
std::cout << "codec test few colors " << std::endl;
Image image;
image.width = 20;
image.height = 20;
image.colorType = LCT_RGBA;
image.bitDepth = 8;
image.data.resize(image.width * image.height * 4);
std::vector<unsigned char> colors;
colors.push_back(0); colors.push_back(0); colors.push_back(0); colors.push_back(255); // black
colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(255); // white
colors.push_back(128); colors.push_back(128); colors.push_back(128); colors.push_back(255); // grey
colors.push_back(0); colors.push_back(0); colors.push_back(255); colors.push_back(255); // blue
colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(0); // transparent white
colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(1); // translucent white
for(size_t i = 0; i < colors.size(); i += 4)
for(size_t j = 0; j < colors.size(); j += 4)
for(size_t k = 0; k < colors.size(); k += 4)
for(size_t l = 0; l < colors.size(); l += 4)
{
//std::cout << (i/4) << " " << (j/4) << " " << (k/4) << " " << (l/4) << std::endl;
for(size_t c = 0; c < 4; c++)
{
for(unsigned y = 0; y < image.height; y++)
for(unsigned x = 0; x < image.width; x++)
{
image.data[y * image.width * 4 + x * 4 + c] = (x ^ y) ? colors[i + c] : colors[j + c];
}
image.data[c] = colors[k + c];
image.data[image.data.size() - 4 + c] = colors[l + c];
}
doCodecTest(image);
}
}
void testSize(unsigned w, unsigned h)
{
std::cout << "codec test size " << w << " " << h << std::endl;
Image image;
image.width = w;
image.height = h;
image.colorType = LCT_RGBA;
image.bitDepth = 8;
image.data.resize(w * h * 4);
for(size_t y = 0; y < h; y++)
for(size_t x = 0; x < w; x++)
{
image.data[w * 4 * y + 4 * x + 0] = x % 256;
image.data[w * 4 * y + 4 * x + 0] = y % 256;
image.data[w * 4 * y + 4 * x + 0] = 255;
image.data[w * 4 * y + 4 * x + 0] = 255;
}
doCodecTest(image);
}
void testPNGCodec()
{
codecTest(1, 1);
codecTest(2, 2);
codecTest(1, 1, LCT_GREY, 1);
codecTest(7, 7, LCT_GREY, 1);
#ifndef DISABLE_SLOW
codecTest(127, 127);
codecTest(127, 127, LCT_GREY, 1);
codecTest(500, 500);
codecTest(1, 10000);
codecTest(10000, 1);
testOtherPattern1();
testOtherPattern2();
#endif // DISABLE_SLOW
testColor(255, 255, 255, 255);
testColor(0, 0, 0, 255);
testColor(1, 2, 3, 255);
testColor(255, 0, 0, 255);
testColor(0, 255, 0, 255);
testColor(0, 0, 255, 255);
testColor(0, 0, 0, 255);
testColor(1, 1, 1, 255);
testColor(1, 1, 1, 1);
testColor(0, 0, 0, 128);
testColor(255, 0, 0, 128);
testColor(127, 127, 127, 255);
testColor(128, 128, 128, 255);
testColor(127, 127, 127, 128);
testColor(128, 128, 128, 128);
//transparent single pixels
testColor(0, 0, 0, 0);
testColor(255, 0, 0, 0);
testColor(1, 2, 3, 0);
testColor(255, 255, 255, 0);
testColor(254, 254, 254, 0);
// This is mainly to test the Adam7 interlacing
for(unsigned h = 1; h < 12; h++)
for(unsigned w = 1; w < 12; w++)
{
testSize(w, h);
}
}
//Tests some specific color conversions with specific color bit combinations
void testColorConvert()
{
//test color conversions to RGBA8
colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111 11111111", LCT_RGBA, 8);
colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010 11111111", LCT_RGBA, 8);
colorConvertTest("1001", LCT_GREY, 4, "10011001 10011001 10011001 11111111", LCT_RGBA, 8);
colorConvertTest("10010101", LCT_GREY, 8, "10010101 10010101 10010101 11111111", LCT_RGBA, 8);
colorConvertTest("10010101 11111110", LCT_GREY_ALPHA, 8, "10010101 10010101 10010101 11111110", LCT_RGBA, 8);
colorConvertTest("10010101 00000001 11111110 00000001", LCT_GREY_ALPHA, 16, "10010101 10010101 10010101 11111110", LCT_RGBA, 8);
colorConvertTest("01010101 00000000 00110011", LCT_RGB, 8, "01010101 00000000 00110011 11111111", LCT_RGBA, 8);
colorConvertTest("01010101 00000000 00110011 10101010", LCT_RGBA, 8, "01010101 00000000 00110011 10101010", LCT_RGBA, 8);
colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011", LCT_RGB, 16, "10101010 11111111 11001100 11111111", LCT_RGBA, 8);
colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011 11100111 00011000", LCT_RGBA, 16, "10101010 11111111 11001100 11100111", LCT_RGBA, 8);
//test color conversions to RGB8
colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111", LCT_RGB, 8);
colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010", LCT_RGB, 8);
colorConvertTest("1001", LCT_GREY, 4, "10011001 10011001 10011001", LCT_RGB, 8);
colorConvertTest("10010101", LCT_GREY, 8, "10010101 10010101 10010101", LCT_RGB, 8);
colorConvertTest("10010101 11111110", LCT_GREY_ALPHA, 8, "10010101 10010101 10010101", LCT_RGB, 8);
colorConvertTest("10010101 00000001 11111110 00000001", LCT_GREY_ALPHA, 16, "10010101 10010101 10010101", LCT_RGB, 8);
colorConvertTest("01010101 00000000 00110011", LCT_RGB, 8, "01010101 00000000 00110011", LCT_RGB, 8);
colorConvertTest("01010101 00000000 00110011 10101010", LCT_RGBA, 8, "01010101 00000000 00110011", LCT_RGB, 8);
colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011", LCT_RGB, 16, "10101010 11111111 11001100", LCT_RGB, 8);
colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011 11100111 00011000", LCT_RGBA, 16, "10101010 11111111 11001100", LCT_RGB, 8);
//test color conversions to RGBA16
colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111", LCT_RGBA, 16);
colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010 10101010 10101010 10101010 11111111 11111111", LCT_RGBA, 16);
//test greyscale color conversions
colorConvertTest("1", LCT_GREY, 1, "11111111", LCT_GREY, 8);
colorConvertTest("1", LCT_GREY, 1, "1111111111111111", LCT_GREY, 16);
colorConvertTest("0", LCT_GREY, 1, "00000000", LCT_GREY, 8);
colorConvertTest("0", LCT_GREY, 1, "0000000000000000", LCT_GREY, 16);
colorConvertTest("11", LCT_GREY, 2, "11111111", LCT_GREY, 8);
colorConvertTest("11", LCT_GREY, 2, "1111111111111111", LCT_GREY, 16);
colorConvertTest("10", LCT_GREY, 2, "10101010", LCT_GREY, 8);
colorConvertTest("10", LCT_GREY, 2, "1010101010101010", LCT_GREY, 16);
colorConvertTest("1000", LCT_GREY, 4, "10001000", LCT_GREY, 8);
colorConvertTest("1000", LCT_GREY, 4, "1000100010001000", LCT_GREY, 16);
colorConvertTest("10110101", LCT_GREY, 8, "1011010110110101", LCT_GREY, 16);
colorConvertTest("1011010110110101", LCT_GREY, 16, "10110101", LCT_GREY, 8);
//others
colorConvertTest("11111111 11111111 11111111 00000000 00000000 00000000", LCT_RGB, 8, "10", LCT_GREY, 1);
colorConvertTest("11111111 11111111 11111111 11111111 11111111 11111111 00000000 00000000 00000000 00000000 00000000 00000000", LCT_RGB, 16, "10", LCT_GREY, 1);
}
//This tests color conversions from any color model to any color model, with any bit depth
//But it tests only with colors black and white, because that are the only colors every single model supports
void testColorConvert2()
{
std::cout << "testColorConvert2" << std::endl;
struct Combo
{
LodePNGColorType colortype;
unsigned bitdepth;
};
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},
};
lodepng::State state;
LodePNGColorMode& mode_in = state.info_png.color;
LodePNGColorMode& mode_out = state.info_raw;
LodePNGColorMode mode_8;
lodepng_color_mode_init(&mode_8);
for(size_t i = 0; i < 256; i++)
{
size_t j = i == 1 ? 255 : i;
lodepng_palette_add(&mode_in, j, j, j, 255);
lodepng_palette_add(&mode_out, j, j, j, 255);
}
for(size_t i = 0; i < 15; i++)
{
mode_in.colortype = combos[i].colortype;
mode_in.bitdepth = combos[i].bitdepth;
for(size_t j = 0; j < 15; j++)
{
mode_out.colortype = combos[i].colortype;
mode_out.bitdepth = combos[i].bitdepth;
unsigned char eight[36] = {
0,0,0,255, 255,255,255,255,
0,0,0,255, 255,255,255,255,
255,255,255,255, 0,0,0,255,
255,255,255,255, 255,255,255,255,
0,0,0,255 }; //input in RGBA8
unsigned char in[72]; //custom input color type