diff options
-rw-r--r-- | LICENSE | 21 | ||||
-rw-r--r-- | lodepng.cpp | 87 | ||||
-rw-r--r-- | lodepng.h | 14 | ||||
-rw-r--r-- | lodepng_benchmark.cpp | 8 | ||||
-rw-r--r-- | lodepng_unittest.cpp | 66 | ||||
-rw-r--r-- | lodepng_util.cpp | 10 | ||||
-rw-r--r-- | lodepng_util.h | 2 | ||||
-rw-r--r-- | pngdetail.cpp | 81 |
8 files changed, 203 insertions, 86 deletions
@@ -0,0 +1,21 @@ +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. + diff --git a/lodepng.cpp b/lodepng.cpp index 70381fb..c62a22f 100644 --- a/lodepng.cpp +++ b/lodepng.cpp @@ -1,7 +1,7 @@ /* -LodePNG version 20161127 +LodePNG version 20180114 -Copyright (c) 2005-2016 Lode Vandevenne +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 @@ -39,7 +39,7 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ #endif /*_MSC_VER */ -const char* LODEPNG_VERSION_STRING = "20161127"; +const char* LODEPNG_VERSION_STRING = "20180114"; /* This source file is built up in the following large parts. The code sections @@ -1171,7 +1171,7 @@ static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size code_d = huffmanDecodeSymbol(in, bp, &tree_d, inbitlength); if(code_d > 29) { - if(code_ll == (unsigned)(-1)) /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + if(code_d == (unsigned)(-1)) /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ @@ -3463,6 +3463,7 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, size_t i; ColorTree tree; size_t numpixels = w * h; + unsigned error = 0; if(lodepng_color_mode_equal(mode_out, mode_in)) { @@ -3516,7 +3517,8 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, for(i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); - CERROR_TRY_RETURN(rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a)); + error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); + if (error) break; } } @@ -3525,7 +3527,7 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, color_tree_cleanup(&tree); } - return 0; /*no error*/ + return error; } #ifdef LODEPNG_COMPILE_ENCODER @@ -4563,12 +4565,20 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* data; /*the data in the chunk*/ /*error: size of the in buffer too small to contain next chunk*/ - if((size_t)((chunk - in) + 12) > insize || chunk < in) CERROR_BREAK(state->error, 30); + if((size_t)((chunk - in) + 12) > insize || chunk < in) + { + if(state->decoder.ignore_end) break; /*other errors may still happen though*/ + CERROR_BREAK(state->error, 30); + } /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ chunkLength = lodepng_chunk_length(chunk); /*error: chunk length larger than the max PNG chunk size*/ - if(chunkLength > 2147483647) CERROR_BREAK(state->error, 63); + if(chunkLength > 2147483647) + { + if(state->decoder.ignore_end) break; /*other errors may still happen though*/ + CERROR_BREAK(state->error, 63); + } if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) { @@ -4655,7 +4665,10 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ - if(!lodepng_chunk_ancillary(chunk)) CERROR_BREAK(state->error, 69); + if(!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) + { + CERROR_BREAK(state->error, 69); + } unknown = 1; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS @@ -4820,6 +4833,8 @@ void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) settings->remember_unknown_chunks = 0; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ settings->ignore_crc = 0; + settings->ignore_critical = 0; + settings->ignore_end = 0; lodepng_decompress_settings_init(&settings->zlibsettings); } @@ -5651,22 +5666,12 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, *outsize = 0; state->error = 0; - lodepng_info_init(&info); - lodepng_info_copy(&info, &state->info_png); - - if((info.color.colortype == LCT_PALETTE || state->encoder.force_palette) - && (info.color.palettesize == 0 || info.color.palettesize > 256)) - { - state->error = 68; /*invalid palette size, it is only allowed to be 1-256*/ - return state->error; - } - - if(state->encoder.auto_convert) + /*check input values validity*/ + if((state->info_png.color.colortype == LCT_PALETTE || state->encoder.force_palette) + && (state->info_png.color.palettesize == 0 || state->info_png.color.palettesize > 256)) { - state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); + CERROR_RETURN_ERROR(state->error, 68); /*invalid palette size, it is only allowed to be 1-256*/ } - if(state->error) return state->error; - if(state->encoder.zlibsettings.btype > 2) { CERROR_RETURN_ERROR(state->error, 61); /*error: unexisting btype*/ @@ -5675,28 +5680,38 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, { CERROR_RETURN_ERROR(state->error, 71); /*error: unexisting interlace mode*/ } - - state->error = checkColorValidity(info.color.colortype, info.color.bitdepth); + state->error = checkColorValidity(state->info_png.color.colortype, state->info_png.color.bitdepth); if(state->error) return state->error; /*error: unexisting color type given*/ state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); if(state->error) return state->error; /*error: unexisting color type given*/ - if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) + /* color convert and compute scanline filter types */ + lodepng_info_init(&info); + lodepng_info_copy(&info, &state->info_png); + if(state->encoder.auto_convert) { - unsigned char* converted; - size_t size = (w * h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; - - converted = (unsigned char*)lodepng_malloc(size); - if(!converted && size) state->error = 83; /*alloc fail*/ - if(!state->error) + state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); + } + if (!state->error) + { + if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) { - state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); + unsigned char* converted; + size_t size = (w * h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; + + converted = (unsigned char*)lodepng_malloc(size); + if(!converted && size) state->error = 83; /*alloc fail*/ + if(!state->error) + { + state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); + } + if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); + lodepng_free(converted); } - if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); - lodepng_free(converted); + else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); } - else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); + /* output all PNG chunks */ ucvector_init(&outv); while(!state->error) /*while only executed once, to break on error*/ { @@ -1,7 +1,7 @@ /* -LodePNG version 20161127 +LodePNG version 20180114 -Copyright (c) 2005-2016 Lode Vandevenne +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 @@ -255,6 +255,7 @@ const char* lodepng_error_text(unsigned code); typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; struct LodePNGDecompressSettings { + /* Check LodePNGDecoderSettings for more ignorable errors */ unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ /*use custom zlib decoder instead of built in one (default: null)*/ @@ -520,7 +521,10 @@ typedef struct LodePNGDecoderSettings { LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ + /* Check LodePNGDecompressSettings for more ignorable errors */ unsigned ignore_crc; /*ignore CRC checksums*/ + unsigned ignore_critical; /*ignore unknown critical chunks*/ + unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/ unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ @@ -1567,6 +1571,8 @@ For decoding: state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums state.decoder.zlibsettings.custom_...: use custom inflate function state.decoder.ignore_crc: ignore CRC checksums +state.decoder.ignore_critical: ignore unknown critical chunks +state.decoder.ignore_end: ignore missing IEND chunk. May fail if this corruption causes other errors state.decoder.color_convert: convert internal PNG color to chosen one state.decoder.read_text_chunks: whether to read in text metadata chunks state.decoder.remember_unknown_chunks: whether to read in unknown chunks @@ -1608,6 +1614,8 @@ yyyymmdd. Some changes aren't backwards compatible. Those are indicated with a (!) symbol. +*) 14 jan 2018: allow optionally ignoring a few more recoverable errors +*) 17 sep 2017: fix memory leak for some encoder input error cases *) 27 nov 2016: grey+alpha auto color model detection bugfix *) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort). *) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within @@ -1757,5 +1765,5 @@ Domain: gmail dot com. Account: lode dot vandevenne. -Copyright (c) 2005-2016 Lode Vandevenne +Copyright (c) 2005-2017 Lode Vandevenne */ diff --git a/lodepng_benchmark.cpp b/lodepng_benchmark.cpp index 791ced0..66de123 100644 --- a/lodepng_benchmark.cpp +++ b/lodepng_benchmark.cpp @@ -499,10 +499,10 @@ int main(int argc, char *argv[]) } } - std::cout << "Total decoding time: " << total_dec_time/NUM_DECODE << "s" << std::endl; - std::cout << "Total encoding time: " << total_enc_time << "s" << std::endl; - std::cout << "Total input size : " << total_in_size << std::endl; - std::cout << "Total encoded size: " << total_enc_size << std::endl; + std::cout << "Total decoding time: " << total_dec_time/NUM_DECODE << "s (" << ((total_in_size/1024.0/1024.0)/(total_dec_time/NUM_DECODE)) << " MB/s)" << std::endl; + std::cout << "Total encoding time: " << total_enc_time << "s (" << ((total_in_size/1024.0/1024.0)/(total_enc_time)) << " MB/s)" << std::endl; + std::cout << "Total uncompressed size : " << total_in_size << std::endl; + std::cout << "Total encoded size: " << total_enc_size << " (" << (100.0 * total_enc_size / total_in_size) << "%)" << std::endl; if(verbose) std::cout << "benchmark done" << std::endl; } diff --git a/lodepng_unittest.cpp b/lodepng_unittest.cpp index 5297a35..975f2b4 100644 --- a/lodepng_unittest.cpp +++ b/lodepng_unittest.cpp @@ -28,6 +28,8 @@ freely, subject to the following restrictions: /* Testing instructions: +*) Ensure no tests commented out below or early return in doMain + *) Compile with g++ with all warnings and run the unit test g++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wshadow -pedantic -ansi -O3 && ./a.out @@ -760,7 +762,8 @@ void testColorConvert() colorConvertTest("1011010110110101", LCT_GREY, 16, "10110101", LCT_GREY, 8); //others - colorConvertTest("11111111 11111111 11111111 00000000 00000000 00000000", LCT_RGB, 1, "10", LCT_GREY, 1); + 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 @@ -1712,26 +1715,59 @@ void testPredefinedFilters() { for(size_t i = 0; i < h; i++) ASSERT_EQUALS(3, outfilters[i]); } -void testWrongWindowSizeGivesError() { +void testEncoderErrors() { + std::cout << "testEncoderErrors" << std::endl; + std::vector<unsigned char> png; unsigned w = 32, h = 32; Image image; generateTestImage(image, w, h); - unsigned error = 0; + + lodepng::State def; lodepng::State state; + + ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state)); + + // test window sizes state.encoder.zlibsettings.windowsize = 0; - error = lodepng::encode(png, &image.data[0], w, h, state); - ASSERT_EQUALS(60, error); + ASSERT_EQUALS(60, lodepng::encode(png, &image.data[0], w, h, state)); state.encoder.zlibsettings.windowsize = 65536; - error = lodepng::encode(png, &image.data[0], w, h, state); - ASSERT_EQUALS(60, error); + ASSERT_EQUALS(60, lodepng::encode(png, &image.data[0], w, h, state)); state.encoder.zlibsettings.windowsize = 1000; // not power of two - error = lodepng::encode(png, &image.data[0], w, h, state); - ASSERT_EQUALS(90, error); + ASSERT_EQUALS(90, lodepng::encode(png, &image.data[0], w, h, state)); state.encoder.zlibsettings.windowsize = 256; - error = lodepng::encode(png, &image.data[0], w, h, state); - ASSERT_EQUALS(0, error); + ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state)); + + state = def; + state.info_png.color.bitdepth = 3; + ASSERT_EQUALS(37, lodepng::encode(png, &image.data[0], w, h, state)); + + state = def; + state.info_png.color.colortype = (LodePNGColorType)5; + ASSERT_EQUALS(31, lodepng::encode(png, &image.data[0], w, h, state)); + + state = def; + state.info_png.color.colortype = LCT_PALETTE; + ASSERT_EQUALS(68, lodepng::encode(png, &image.data[0], w, h, state)); + + state = def; + state.info_png.interlace_method = 0; + ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state)); + state.info_png.interlace_method = 1; + ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state)); + state.info_png.interlace_method = 2; + ASSERT_EQUALS(71, lodepng::encode(png, &image.data[0], w, h, state)); + + state = def; + state.encoder.zlibsettings.btype = 0; + ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state)); + state.encoder.zlibsettings.btype = 1; + ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state)); + state.encoder.zlibsettings.btype = 2; + ASSERT_EQUALS(0, lodepng::encode(png, &image.data[0], w, h, state)); + state.encoder.zlibsettings.btype = 3; + ASSERT_EQUALS(61, lodepng::encode(png, &image.data[0], w, h, state)); } void addColor(std::vector<unsigned char>& colors, unsigned char r, unsigned char g, unsigned char b, unsigned char a) @@ -1996,26 +2032,26 @@ void testPaletteToPaletteDecode2() { lodepng_palette_add(&state.info_raw, 3, 3, 3, 255); unsigned char* image2 = 0; unsigned error2 = lodepng_decode(&image2, &width, &height, &state, &png[0], png.size()); - ASSERT_EQUALS(82, error2); lodepng_state_cleanup(&state); + ASSERT_EQUALS(82, error2); free(image2); } void doMain() { //PNG - testPNGCodec(); + testPNGCodec(); // this one is slow for valgrind testPngSuiteTiny(); testPaletteFilterTypesZero(); testComplexPNG(); testPredefinedFilters(); testFuzzing(); - testWrongWindowSizeGivesError(); + testEncoderErrors(); testPaletteToPaletteDecode(); testPaletteToPaletteDecode2(); //Colors - testFewColors(); + testFewColors(); // this one is slow for valgrind testColorKeyConvert(); testColorConvert(); testColorConvert2(); diff --git a/lodepng_util.cpp b/lodepng_util.cpp index 76efdea..6fa1618 100644 --- a/lodepng_util.cpp +++ b/lodepng_util.cpp @@ -1,7 +1,7 @@ /* LodePNG Utils -Copyright (c) 2005-2014 Lode Vandevenne +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 @@ -52,9 +52,9 @@ unsigned getChunkInfo(std::vector<std::string>& names, std::vector<size_t>& size if(std::string(type).size() != 4) return 1; unsigned length = lodepng_chunk_length(chunk); - if(chunk + length + 12 > end) return 1; names.push_back(type); sizes.push_back(length); + if(chunk + length + 12 > end) return 1; next = lodepng_chunk_next_const(chunk); if (next <= chunk) return 1; // integer overflow @@ -185,7 +185,7 @@ unsigned getFilterTypesInterlaced(std::vector<std::vector<unsigned char> >& filt { char type[5]; lodepng_chunk_type(type, chunk); - if(std::string(type).size() != 4) return 1; //Probably not a PNG file + if(std::string(type).size() != 4) break; //Probably not a PNG file if(std::string(type) == "IDAT") { @@ -203,11 +203,11 @@ unsigned getFilterTypesInterlaced(std::vector<std::vector<unsigned char> >& filt } next = lodepng_chunk_next_const(chunk); - if (next <= chunk) return 1; // integer overflow + if (next <= chunk) break; // integer overflow chunk = next; } - //Decompress all IDAT data + //Decompress all IDAT data (if the while loop ended early, this might fail) std::vector<unsigned char> data; error = lodepng::decompress(data, &zdata[0], zdata.size()); diff --git a/lodepng_util.h b/lodepng_util.h index 236ad46..19fcbcb 100644 --- a/lodepng_util.h +++ b/lodepng_util.h @@ -1,7 +1,7 @@ /* LodePNG Utils -Copyright (c) 2005-2014 Lode Vandevenne +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 diff --git a/pngdetail.cpp b/pngdetail.cpp index 41952a4..b11d78c 100644 --- a/pngdetail.cpp +++ b/pngdetail.cpp @@ -1,7 +1,7 @@ /* LodePNG pngdetail -Copyright (c) 2005-2015 Lode Vandevenne +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 @@ -184,7 +184,13 @@ void displayChunkNames(const std::vector<unsigned char>& buffer, const Options& std::vector<std::string> names; std::vector<size_t> sizes; unsigned error = lodepng::getChunkInfo(names, sizes, buffer); - if(error) std::cout << "Error while identifying chunks. Listing identified chunks anyway." << std::endl; + if(error) { + if(!names.empty() && names.back() == "IEND" && sizes.back() == 0) { + std::cout << "Corruption or superfluous data detected after the IEND chunk" << std::endl; + } else { + std::cout << "Error while identifying chunks. Listing identified chunks anyway." << std::endl; + } + } if(options.show_chunks2) { @@ -711,7 +717,7 @@ void showHelp() "-b: show Zlib blocks\n" "-B: show Zlib block symbol counts\n" "-7: show all lz77 values (huge output)\n" - "-x: print most numbers in hexadecimal\n" + "-x: print most integer numbers in hexadecimal (includes e.g. year, num unique colors, ...)\n" << std::endl; } @@ -797,6 +803,11 @@ unsigned showFileInfo(const std::string& filename, const Options& options) std::vector<unsigned char> image; unsigned w, h; + if(options.show_png_info) + { + std::cout << "pngdetail version: " << LODEPNG_VERSION_STRING << std::endl; + } + unsigned error = lodepng::load_file(buffer, filename); //load the image file with given filename if(error) @@ -810,27 +821,45 @@ unsigned showFileInfo(const std::string& filename, const Options& options) state.info_raw.bitdepth = 16; error = lodepng::decode(image, w, h, state, buffer); - // In case of checksum errors, disable checksums - while (error == 57 || error == 58) { + // In case of checksum errors and some other ignorable errors, report it but ignore it and retry + while (error) { + std::cerr << "Decoder error " << error << ": " << lodepng_error_text(error) << std::endl; + unsigned error2 = error; if(error == 57) { - std::cout << "Error: invalid CRC checksum" << std::endl; + std::cerr << "Ignoring the error: enabling ignore_crc" << std::endl; state.decoder.ignore_crc = 1; error = lodepng::decode(image, w, h, state, buffer); } - - if(error == 58) + else if(error == 58) { - std::cout << "Error: invalid Adler32 checksum" << std::endl; + std::cerr << "Ignoring the error: enabling ignore_adler32" << std::endl; state.decoder.zlibsettings.ignore_adler32 = 1; error = lodepng::decode(image, w, h, state, buffer); } - } - - if(error) - { - std::cout << "Decoder error " << error << ": " << lodepng_error_text(error) << std::endl; - // Do not return: some sections may still show partial info about a corrupted PNG. + else if(error == 69) + { + std::cerr << "Ignoring the error: enabling ignore_critical" << std::endl; + state.decoder.ignore_critical = 1; + error = lodepng::decode(image, w, h, state, buffer); + } + else if(error == 30 || error == 63) + { + std::cerr << "Ignoring the error: enabling ignore_end" << std::endl; + state.decoder.ignore_end = 1; + error = lodepng::decode(image, w, h, state, buffer); + } + else + { + if(error == 0) std::cerr << "This error is unrecoverable" << std::endl; + break; // other error that we cannot ignore + } + if(error == 0) std::cerr << "Successfully ignored the error" << std::endl; + if(error == error2) + { + std::cerr << "Failed to ignore the error" << std::endl; + break; // avoid infinite loop if ignoring did not fix the error code + } } bool extra_newlines = false; @@ -850,15 +879,23 @@ unsigned showFileInfo(const std::string& filename, const Options& options) std::cout << "Height: " << h << std::endl; if(options.show_extra_png_info) std::cout << "Num pixels: " << w * h << std::endl; + std::cout << "Num unique colors: " << countColors(image, w, h) << std::endl; if(options.show_extra_png_info && w > 0 && h > 0) { - std::ios_base::fmtflags flags = std::cout.flags(); - std::cout << "Top left pixel color: #" - << std::hex << std::setfill('0') - << std::setw(2) << (int)image[0] << std::setw(2) << (int)image[1] << std::setw(2) << (int)image[2] << std::setw(2) << (int)image[3] - << std::endl; - std::cout.flags(flags); - std::cout << "Num unique colors: " << countColors(image, w, h) << std::endl; + double r = 0, g = 0, b = 0, a = 0; + for(unsigned y = 0; y < h; y++) { + for(unsigned x = 0; x < w; x++) { + r += 256 * image[y * 8 * w + x * 8 + 0] + image[y * 8 * w + x * 8 + 1]; + g += 256 * image[y * 8 * w + x * 8 + 2] + image[y * 8 * w + x * 8 + 3]; + b += 256 * image[y * 8 * w + x * 8 + 4] + image[y * 8 * w + x * 8 + 5]; + a += 256 * image[y * 8 * w + x * 8 + 6] + image[y * 8 * w + x * 8 + 7]; + } + } + r /= (w * h * 257.0); + g /= (w * h * 257.0); + b /= (w * h * 257.0); + a /= (w * h * 257.0); + std::cout << "Average color: " << r << ", " << g << ", " << b << ", " << a << std::endl; } displayPNGInfo(state.info_png, options); |