From 727af1238f305aebd00f5889dcb8e1d6f2baf018 Mon Sep 17 00:00:00 2001 From: vsonnier Date: Fri, 10 Aug 2018 06:49:05 +0200 Subject: [PATCH] Updated LodePNG to 2018.08.09 version --- external/lodepng/lodepng.cpp | 350 ++++++++++++++++++++++++++++++----- external/lodepng/lodepng.h | 124 +++++++++++-- 2 files changed, 411 insertions(+), 63 deletions(-) diff --git a/external/lodepng/lodepng.cpp b/external/lodepng/lodepng.cpp index 6568bba..f2e0864 100644 --- a/external/lodepng/lodepng.cpp +++ b/external/lodepng/lodepng.cpp @@ -1,5 +1,5 @@ /* -LodePNG version 20180611 +LodePNG version 20180809 Copyright (c) 2005-2018 Lode Vandevenne @@ -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 = "20180611"; +const char* LODEPNG_VERSION_STRING = "20180809"; /* This source file is built up in the following large parts. The code sections @@ -283,42 +283,29 @@ static unsigned ucvector_push_back(ucvector* p, unsigned char c) #ifdef LODEPNG_COMPILE_PNG #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned string_resize(char** out, size_t size) -{ - char* data = (char*)lodepng_realloc(*out, size + 1); - if(data) - { - data[size] = 0; /*null termination char*/ - *out = data; - } - return data != 0; -} -/*init a {char*, size_t} pair for use as string*/ -static void string_init(char** out) -{ - *out = NULL; - string_resize(out, 0); -} - -/*free the above pair again*/ +/*free string pointer and set it to NULL*/ static void string_cleanup(char** out) { lodepng_free(*out); *out = NULL; } -static void string_set(char** out, const char* in) +/* dynamically allocates a new string with a copy of the null terminated input text */ +static char* alloc_string(const char* in) { - size_t insize = strlen(in), i; - if(string_resize(out, insize)) + size_t insize = strlen(in); + char* out = (char*)lodepng_malloc(insize + 1); + if(out) { + size_t i; for(i = 0; i != insize; ++i) { - (*out)[i] = in[i]; + out[i] = in[i]; } + out[i] = 0; } + return out; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_PNG*/ @@ -2871,11 +2858,8 @@ unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) info->text_keys = new_keys; info->text_strings = new_strings; - string_init(&info->text_keys[info->text_num - 1]); - string_set(&info->text_keys[info->text_num - 1], key); - - string_init(&info->text_strings[info->text_num - 1]); - string_set(&info->text_strings[info->text_num - 1], str); + info->text_keys[info->text_num - 1] = alloc_string(key); + info->text_strings[info->text_num - 1] = alloc_string(str); return 0; } @@ -2950,20 +2934,42 @@ unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langt info->itext_transkeys = new_transkeys; info->itext_strings = new_strings; - string_init(&info->itext_keys[info->itext_num - 1]); - string_set(&info->itext_keys[info->itext_num - 1], key); - - string_init(&info->itext_langtags[info->itext_num - 1]); - string_set(&info->itext_langtags[info->itext_num - 1], langtag); - - string_init(&info->itext_transkeys[info->itext_num - 1]); - string_set(&info->itext_transkeys[info->itext_num - 1], transkey); - - string_init(&info->itext_strings[info->itext_num - 1]); - string_set(&info->itext_strings[info->itext_num - 1], str); + info->itext_keys[info->itext_num - 1] = alloc_string(key); + info->itext_langtags[info->itext_num - 1] = alloc_string(langtag); + info->itext_transkeys[info->itext_num - 1] = alloc_string(transkey); + info->itext_strings[info->itext_num - 1] = alloc_string(str); return 0; } + +/* same as set but does not delete */ +static unsigned lodepng_assign_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) +{ + info->iccp_name = alloc_string(name); + info->iccp_profile = (unsigned char*)lodepng_malloc(profile_size); + + if(!info->iccp_name || !info->iccp_profile) return 83; /*alloc fail*/ + + memcpy(info->iccp_profile, profile, profile_size); + info->iccp_profile_size = profile_size; + + return 0; /*ok*/ +} + +unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) +{ + if(info->iccp_name) lodepng_clear_icc(info); + + return lodepng_assign_icc(info, name, profile, profile_size); +} + +void lodepng_clear_icc(LodePNGInfo* info) +{ + string_cleanup(&info->iccp_name); + lodepng_free(info->iccp_profile); + info->iccp_profile = NULL; + info->iccp_profile_size = 0; +} #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ void lodepng_info_init(LodePNGInfo* info) @@ -2982,6 +2988,13 @@ void lodepng_info_init(LodePNGInfo* info) info->time_defined = 0; info->phys_defined = 0; + info->gama_defined = 0; + info->chrm_defined = 0; + info->srgb_defined = 0; + info->iccp_defined = 0; + info->iccp_name = NULL; + info->iccp_profile = NULL; + LodePNGUnknownChunks_init(info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } @@ -2993,6 +3006,8 @@ void lodepng_info_cleanup(LodePNGInfo* info) LodePNGText_cleanup(info); LodePNGIText_cleanup(info); + lodepng_clear_icc(info); + LodePNGUnknownChunks_cleanup(info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } @@ -3007,6 +3022,10 @@ unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); + if(source->iccp_defined) + { + CERROR_TRY_RETURN(lodepng_assign_icc(dest, source->iccp_name, source->iccp_profile, source->iccp_profile_size)); + } LodePNGUnknownChunks_init(dest); CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); @@ -3014,13 +3033,6 @@ unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) return 0; } -void lodepng_info_swap(LodePNGInfo* a, LodePNGInfo* b) -{ - LodePNGInfo temp = *a; - *a = *b; - *b = temp; -} - /* ////////////////////////////////////////////////////////////////////////// */ /*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ @@ -4465,7 +4477,7 @@ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSetting unsigned length, begin, compressed; char *key = 0, *langtag = 0, *transkey = 0; ucvector decoded; - ucvector_init(&decoded); + ucvector_init(&decoded); /* TODO: only use in case of compressed text */ while(!error) /*not really a while loop, only used to break on error*/ { @@ -4575,6 +4587,88 @@ static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, siz return 0; /* OK */ } + +static unsigned readChunk_gAMA(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 4) return 96; /*invalid gAMA chunk size*/ + + info->gama_defined = 1; + info->gama_gamma = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; + + return 0; /* OK */ +} + +static unsigned readChunk_cHRM(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 32) return 97; /*invalid cHRM chunk size*/ + + info->chrm_defined = 1; + info->chrm_white_x = 16777216u * data[ 0] + 65536u * data[ 1] + 256u * data[ 2] + data[ 3]; + info->chrm_white_y = 16777216u * data[ 4] + 65536u * data[ 5] + 256u * data[ 6] + data[ 7]; + info->chrm_red_x = 16777216u * data[ 8] + 65536u * data[ 9] + 256u * data[10] + data[11]; + info->chrm_red_y = 16777216u * data[12] + 65536u * data[13] + 256u * data[14] + data[15]; + info->chrm_green_x = 16777216u * data[16] + 65536u * data[17] + 256u * data[18] + data[19]; + info->chrm_green_y = 16777216u * data[20] + 65536u * data[21] + 256u * data[22] + data[23]; + info->chrm_blue_x = 16777216u * data[24] + 65536u * data[25] + 256u * data[26] + data[27]; + info->chrm_blue_y = 16777216u * data[28] + 65536u * data[29] + 256u * data[30] + data[31]; + + return 0; /* OK */ +} + +static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 1) return 98; /*invalid sRGB chunk size (this one is never ignored)*/ + + info->srgb_defined = 1; + info->srgb_intent = data[0]; + + return 0; /* OK */ +} + +static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, + const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + unsigned i; + + unsigned length, string2_begin; + ucvector decoded; + + info->iccp_defined = 1; + if(info->iccp_name) lodepng_clear_icc(info); + + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 2 >= chunkLength) return 75; /*no null termination, corrupt?*/ + if(length < 1 || length > 79) return 89; /*keyword too short or long*/ + + info->iccp_name = (char*)lodepng_malloc(length + 1); + if(!info->iccp_name) return 83; /*alloc fail*/ + + info->iccp_name[length] = 0; + for(i = 0; i != length; ++i) info->iccp_name[i] = (char)data[i]; + + if(data[length + 1] != 0) return 72; /*the 0 byte indicating compression must be 0*/ + + string2_begin = length + 2; + if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/ + + length = (unsigned)chunkLength - string2_begin; + ucvector_init(&decoded); + error = zlib_decompress(&decoded.data, &decoded.size, + (unsigned char*)(&data[string2_begin]), + length, zlibsettings); + if(!error) { + info->iccp_profile_size = decoded.size; + info->iccp_profile = (unsigned char*)lodepng_malloc(decoded.size); + if(info->iccp_profile) { + memcpy(info->iccp_profile, decoded.data, decoded.size); + } else { + error = 83; /* alloc fail */ + } + } + ucvector_cleanup(&decoded); + return error; +} #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ @@ -4640,6 +4734,8 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, data = lodepng_chunk_data_const(chunk); + unknown = 0; + /*IDAT chunk, containing compressed image data*/ if(lodepng_chunk_type_equals(chunk, "IDAT")) { @@ -4716,6 +4812,26 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, state->error = readChunk_pHYs(&state->info_png, data, chunkLength); if(state->error) break; } + else if(lodepng_chunk_type_equals(chunk, "gAMA")) + { + state->error = readChunk_gAMA(&state->info_png, data, chunkLength); + if(state->error) break; + } + else if(lodepng_chunk_type_equals(chunk, "cHRM")) + { + state->error = readChunk_cHRM(&state->info_png, data, chunkLength); + if(state->error) break; + } + else if(lodepng_chunk_type_equals(chunk, "sRGB")) + { + state->error = readChunk_sRGB(&state->info_png, data, chunkLength); + if(state->error) break; + } + else if(lodepng_chunk_type_equals(chunk, "iCCP")) + { + state->error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + if(state->error) break; + } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { @@ -5208,6 +5324,73 @@ static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) return error; } +static unsigned addChunk_gAMA(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector data; + ucvector_init(&data); + + lodepng_add32bitInt(&data, info->gama_gamma); + + error = addChunk(out, "gAMA", data.data, data.size); + ucvector_cleanup(&data); + + return error; +} + +static unsigned addChunk_cHRM(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector data; + ucvector_init(&data); + + lodepng_add32bitInt(&data, info->chrm_white_x); + lodepng_add32bitInt(&data, info->chrm_white_y); + lodepng_add32bitInt(&data, info->chrm_red_x); + lodepng_add32bitInt(&data, info->chrm_red_y); + lodepng_add32bitInt(&data, info->chrm_green_x); + lodepng_add32bitInt(&data, info->chrm_green_y); + lodepng_add32bitInt(&data, info->chrm_blue_x); + lodepng_add32bitInt(&data, info->chrm_blue_y); + + error = addChunk(out, "cHRM", data.data, data.size); + ucvector_cleanup(&data); + + return error; +} + +static unsigned addChunk_sRGB(ucvector* out, const LodePNGInfo* info) +{ + unsigned char data = info->srgb_intent; + return addChunk(out, "sRGB", &data, 1); +} + +static unsigned addChunk_iCCP(ucvector* out, const LodePNGInfo* info, LodePNGCompressSettings* zlibsettings) +{ + unsigned error = 0; + ucvector data, compressed; + size_t i; + + ucvector_init(&data); + ucvector_init(&compressed); + for(i = 0; info->iccp_name[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)info->iccp_name[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&data, 0); /*0 termination char*/ + ucvector_push_back(&data, 0); /*compression method: 0*/ + + error = zlib_compress(&compressed.data, &compressed.size, + info->iccp_profile, info->iccp_profile_size, zlibsettings); + if(!error) + { + for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); + error = addChunk(out, "iCCP", data.data, data.size); + } + + ucvector_cleanup(&compressed); + ucvector_cleanup(&data); + return error; +} + #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, @@ -5704,6 +5887,28 @@ static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t data } return 0; } + +static unsigned isGreyICCProfile(const unsigned char* profile, unsigned size) +{ + /* + It is a grey profile if bytes 16-19 are "GRAY", rgb profile if bytes 16-19 + are "RGB ". We do not perform any full parsing of the ICC profile here, other + than check those 4 bytes to grayscale profile. Other than that, validity of + the profile is not checked. This is needed only because the PNG specification + requires using a non-grey color model if there is an ICC profile with "RGB " + (sadly limiting compression opportunities if the input data is greyscale RGB + data), and requires using a grey color model if it is "GRAY". + */ + if(size < 20) return 0; + return profile[16] == 'G' && profile[17] == 'R' && profile[18] == 'A' && profile[19] == 'Y'; +} + +static unsigned isRGBICCProfile(const unsigned char* profile, unsigned size) +{ + /* See comment in isGreyICCProfile*/ + if(size < 20) return 0; + return profile[16] == 'R' && profile[17] == 'G' && profile[18] == 'B' && profile[19] == ' '; +} #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ unsigned lodepng_encode(unsigned char** out, size_t* outsize, @@ -5746,6 +5951,41 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, { state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(state->info_png.iccp_defined) + { + unsigned grey_icc = isGreyICCProfile(state->info_png.iccp_profile, state->info_png.iccp_profile_size); + unsigned grey_png = info.color.colortype == LCT_GREY || info.color.colortype == LCT_GREY_ALPHA; + /* TODO: perhaps instead of giving errors or less optimal compression, we can automatically modify + the ICC profile here to say "GRAY" or "RGB " to match the PNG color type, unless this will require + non trivial changes to the rest of the ICC profile */ + if(!grey_icc && !isRGBICCProfile(state->info_png.iccp_profile, state->info_png.iccp_profile_size)) + { + CERROR_RETURN_ERROR(state->error, 100); /* Disallowed profile color type for PNG */ + } + if(!state->encoder.auto_convert && grey_icc != grey_png) + { + /* Non recoverable: encoder not allowed to convert color type, and requested color type not + compatible with ICC color type */ + CERROR_RETURN_ERROR(state->error, 101); + } + if(grey_icc && !grey_png) + { + /* Non recoverable: trying to set greyscale ICC profile while colored pixels were given */ + CERROR_RETURN_ERROR(state->error, 102); + /* NOTE: this relies on the fact that lodepng_auto_choose_color never returns palette for greyscale pixels */ + } + if(!grey_icc && grey_png) + { + /* Recoverable but an unfortunate loss in compression density: We have greyscale pixels but + are forced to store them in more expensive RGB format that will repeat each value 3 times + because the PNG spec does not allow an RGB ICC profile with internal greyscale color data */ + if(info.color.colortype == LCT_GREY) info.color.colortype = LCT_RGB; + if(info.color.colortype == LCT_GREY_ALPHA) info.color.colortype = LCT_RGBA; + if(info.color.bitdepth < 8) info.color.bitdepth = 8; + } + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ if (!state->error) { if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) @@ -5783,6 +6023,11 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); if(state->error) break; } + /*color profile chunks must come before PLTE */ + if(info.iccp_defined) addChunk_iCCP(&outv, &info, &state->encoder.zlibsettings); + if(info.srgb_defined) addChunk_sRGB(&outv, &info); + if(info.gama_defined) addChunk_gAMA(&outv, &info); + if(info.chrm_defined) addChunk_cHRM(&outv, &info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*PLTE*/ if(info.color.colortype == LCT_PALETTE) @@ -6068,6 +6313,13 @@ const char* lodepng_error_text(unsigned code) case 93: return "zero width or height is invalid"; case 94: return "header chunk must have a size of 13 bytes"; case 95: return "integer overflow with combined idat chunk size"; + case 96: return "invalid gAMA chunk size"; + case 97: return "invalid cHRM chunk size"; + case 98: return "invalid sRGB chunk size"; + case 99: return "invalid sRGB rendering intent"; + case 100: return "invalid ICC profile color type, the PNG specification only allows RGB or GRAY"; + case 101: return "PNG specification does not allow RGB ICC profile on grey color types and vice versa"; + case 102: return "not allowed to set greyscale ICC profile with colored pixels by PNG specification"; } return "unknown error code"; } diff --git a/external/lodepng/lodepng.h b/external/lodepng/lodepng.h index 6c56c1d..ba46fdc 100644 --- a/external/lodepng/lodepng.h +++ b/external/lodepng/lodepng.h @@ -1,5 +1,5 @@ /* -LodePNG version 20180611 +LodePNG version 20180809 Copyright (c) 2005-2018 Lode Vandevenne @@ -255,7 +255,7 @@ const char* lodepng_error_text(unsigned code); typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; struct LodePNGDecompressSettings { - /* Check LodePNGDecoderSettings for more ignorable errors */ + /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */ 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)*/ @@ -437,6 +437,10 @@ typedef struct LodePNGInfo text_strings, while text_keys are keywords that give a short description what the actual text represents, e.g. Title, Author, Description, or anything else. + All the string fields below including keys, names and language tags are null terminated. + The PNG specification uses null characters for the keys, names and tags, and forbids null + characters to appear in the main text which is why we can use null termination everywhere here. + A keyword is minimum 1 character and maximum 79 characters long. It's discouraged to use a single line length longer than 79 characters for texts. @@ -469,11 +473,86 @@ typedef struct LodePNGInfo unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ /* - unknown chunks - There are 3 buffers, one for each position in the PNG where unknown chunks can appear - each buffer contains all unknown chunks for that position consecutively - The 3 buffers are the unknown chunks between certain critical chunks: - 0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND + Color profile related chunks: gAMA, cHRM, sRGB, iCPP + + LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color + profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please + use these values with a color management library. + + See the PNG, ICC and sRGB specifications for more information about the meaning of these values. + */ + + /* gAMA chunk: optional, overridden by sRGB or iCCP if those are present. */ + unsigned gama_defined; /* Whether a gAMA chunk is present (0 = not present, 1 = present). */ + unsigned gama_gamma; /* Gamma exponent times 100000 */ + + /* cHRM chunk: optional, overridden by sRGB or iCCP if those are present. */ + unsigned chrm_defined; /* Whether a cHRM chunk is present (0 = not present, 1 = present). */ + unsigned chrm_white_x; /* White Point x times 100000 */ + unsigned chrm_white_y; /* White Point y times 100000 */ + unsigned chrm_red_x; /* Red x times 100000 */ + unsigned chrm_red_y; /* Red y times 100000 */ + unsigned chrm_green_x; /* Green x times 100000 */ + unsigned chrm_green_y; /* Green y times 100000 */ + unsigned chrm_blue_x; /* Blue x times 100000 */ + unsigned chrm_blue_y; /* Blue y times 100000 */ + + /* + sRGB chunk: optional. May not appear at the same time as iCCP. + If gAMA is also present gAMA must contain value 45455. + If cHRM is also present cHRM must contain respectively 31270,32900,64000,33000,30000,60000,15000,6000. + */ + unsigned srgb_defined; /* Whether an sRGB chunk is present (0 = not present, 1 = present). */ + unsigned srgb_intent; /* Rendering intent: 0=perceptual, 1=rel. colorimetric, 2=saturation, 3=abs. colorimetric */ + + /* + iCCP chunk: optional. May not appear at the same time as sRGB. + + LodePNG does not parse or use the ICC profile (except its color space header field for an edge case), a + separate library to handle the ICC data (not included in LodePNG) format is needed to use it for color + management and conversions. + + For encoding, if iCCP is present, gAMA and cHRM are recommended to be added as well with values that match the ICC + profile as closely as possible, if you wish to do this you should provide the correct values for gAMA and cHRM and + enable their '_defined' flags since LodePNG will not automatically compute them from the ICC profile. + + For encoding, the ICC profile is required by the PNG specification to be an "RGB" profile for non-grey + PNG color types and a "GRAY" profile for grey PNG color types. If you disable auto_convert, you must ensure + the ICC profile type matches your requested color type, else the encoder gives an error. If auto_convert is + enabled (the default), and the ICC profile is not a good match for the pixel data, this will result in an encoder + error if the pixel data has non-grey pixels for a GRAY profile, or a silent less-optimal compression of the pixel + data if the pixels could be encoded as greyscale but the ICC profile is RGB. + + To avoid this do not set an ICC profile in the image unless there is a good reason for it, and when doing so + make sure you compute it carefully to avoid the above problems. + */ + unsigned iccp_defined; /* Whether an iCCP chunk is present (0 = not present, 1 = present). */ + char* iccp_name; /* Null terminated string with profile name, 1-79 bytes */ + /* + The ICC profile in iccp_profile_size bytes. + Don't allocate this buffer yourself. Use the init/cleanup functions + correctly and use lodepng_set_icc and lodepng_clear_icc. + */ + unsigned char* iccp_profile; + unsigned iccp_profile_size; /* The size of iccp_profile in bytes */ + + /* End of color profile related chunks */ + + + /* + unknown chunks: chunks not known by LodePNG, passed on byte for byte. + + There are 3 buffers, one for each position in the PNG where unknown chunks can appear. + Each buffer contains all unknown chunks for that position consecutively. + The 3 positions are: + 0: between IHDR and PLTE, 1: between PLTE and IDAT, 2: between IDAT and IEND. + + For encoding, do not store critical chunks or known chunks that are enabled with a "_defined" flag + above in here, since the encoder will blindly follow this and could then encode an invalid PNG file + (such as one with two IHDR chunks or the disallowed combination of sRGB with iCCP). But do use + this if you wish to store an ancillary chunk that is not supported by LodePNG (such as sPLT or hIST), + or any non-standard PNG chunk. + Do not allocate or traverse this data yourself. Use the chunk traversing functions declared later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct. */ @@ -489,12 +568,16 @@ void lodepng_info_cleanup(LodePNGInfo* info); unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS -void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/ +void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ -void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/ +void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ + +/*replaces if exists*/ +unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size); +void lodepng_clear_icc(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /* @@ -521,10 +604,14 @@ typedef struct LodePNGDecoderSettings { LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ - /* Check LodePNGDecompressSettings for more ignorable errors */ + /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */ 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, ...)*/ + /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable + errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some + strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters + in string keys, etc... */ unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ @@ -558,8 +645,11 @@ typedef enum LodePNGFilterStrategy LFS_PREDEFINED } LodePNGFilterStrategy; -/*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding. -Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ +/*Gives characteristics about the integer RGBA colors of the image (count, alpha channel usage, bit depth, ...), +which helps decide which color model to use for encoding. +Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms. +NOTE: This is not related to the ICC color profile, search "iccp_profile" instead to find the ICC/chromacity/... +fields in this header file.*/ typedef struct LodePNGColorProfile { unsigned colored; /*not greyscale*/ @@ -575,7 +665,9 @@ typedef struct LodePNGColorProfile void lodepng_color_profile_init(LodePNGColorProfile* profile); -/*Get a LodePNGColorProfile of the image.*/ +/*Get a LodePNGColorProfile of the image +NOTE: This is not related to the ICC color profile, search "iccp_profile" instead to find the ICC/chromacity/... +fields in this header file.*/ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, const unsigned char* image, unsigned w, unsigned h, const LodePNGColorMode* mode_in); @@ -894,13 +986,15 @@ TODO: [.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often [.] check compatibility with various compilers - done but needs to be redone for every newer version [X] converting color to 16-bit per channel types -[ ] read all public PNG chunk types (but never let the color profile and gamma ones touch RGB values) +[X] support color profile chunk types (but never let them touch RGB values by default) +[ ] support all public PNG chunk types [ ] make sure encoder generates no chunks with size > (2^31)-1 [ ] partial decoding (stream processing) [X] let the "isFullyOpaque" function check color keys and transparent palettes too [X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" [ ] don't stop decoding on errors like 69, 57, 58 (make warnings) [ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ... +[ ] errors with line numbers (and version) [ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes [ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... [ ] allow user to give data (void*) to custom allocator @@ -1614,6 +1708,8 @@ yyyymmdd. Some changes aren't backwards compatible. Those are indicated with a (!) symbol. +*) 10 aug 2018: added support for gAMA, cHRM, sRGB and iCCP chunks. This change + is backwards compatible unless you relied on unknown_chunks to use those. *) 11 jun 2018: less restrictive check for pixel size integer overflow *) 14 jan 2018: allow optionally ignoring a few more recoverable errors *) 17 sep 2017: fix memory leak for some encoder input error cases