Updated LodePNG to 2018.08.09 version

This commit is contained in:
vsonnier 2018-08-10 06:49:05 +02:00
parent 5e8e608a9c
commit 727af1238f
2 changed files with 411 additions and 63 deletions

View File

@ -1,5 +1,5 @@
/* /*
LodePNG version 20180611 LodePNG version 20180809
Copyright (c) 2005-2018 Lode Vandevenne 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*/ #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
#endif /*_MSC_VER */ #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 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_PNG
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS #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*/ /*free string pointer and set it to NULL*/
static void string_init(char** out)
{
*out = NULL;
string_resize(out, 0);
}
/*free the above pair again*/
static void string_cleanup(char** out) static void string_cleanup(char** out)
{ {
lodepng_free(*out); lodepng_free(*out);
*out = NULL; *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; size_t insize = strlen(in);
if(string_resize(out, insize)) char* out = (char*)lodepng_malloc(insize + 1);
if(out)
{ {
size_t i;
for(i = 0; i != insize; ++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_ANCILLARY_CHUNKS*/
#endif /*LODEPNG_COMPILE_PNG*/ #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_keys = new_keys;
info->text_strings = new_strings; info->text_strings = new_strings;
string_init(&info->text_keys[info->text_num - 1]); info->text_keys[info->text_num - 1] = alloc_string(key);
string_set(&info->text_keys[info->text_num - 1], key); info->text_strings[info->text_num - 1] = alloc_string(str);
string_init(&info->text_strings[info->text_num - 1]);
string_set(&info->text_strings[info->text_num - 1], str);
return 0; 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_transkeys = new_transkeys;
info->itext_strings = new_strings; info->itext_strings = new_strings;
string_init(&info->itext_keys[info->itext_num - 1]); info->itext_keys[info->itext_num - 1] = alloc_string(key);
string_set(&info->itext_keys[info->itext_num - 1], key); info->itext_langtags[info->itext_num - 1] = alloc_string(langtag);
info->itext_transkeys[info->itext_num - 1] = alloc_string(transkey);
string_init(&info->itext_langtags[info->itext_num - 1]); info->itext_strings[info->itext_num - 1] = alloc_string(str);
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);
return 0; 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*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
void lodepng_info_init(LodePNGInfo* info) void lodepng_info_init(LodePNGInfo* info)
@ -2982,6 +2988,13 @@ void lodepng_info_init(LodePNGInfo* info)
info->time_defined = 0; info->time_defined = 0;
info->phys_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); LodePNGUnknownChunks_init(info);
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} }
@ -2993,6 +3006,8 @@ void lodepng_info_cleanup(LodePNGInfo* info)
LodePNGText_cleanup(info); LodePNGText_cleanup(info);
LodePNGIText_cleanup(info); LodePNGIText_cleanup(info);
lodepng_clear_icc(info);
LodePNGUnknownChunks_cleanup(info); LodePNGUnknownChunks_cleanup(info);
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} }
@ -3007,6 +3022,10 @@ unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source)
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); CERROR_TRY_RETURN(LodePNGText_copy(dest, source));
CERROR_TRY_RETURN(LodePNGIText_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); LodePNGUnknownChunks_init(dest);
CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source));
@ -3014,13 +3033,6 @@ unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source)
return 0; 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*/ /*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; unsigned length, begin, compressed;
char *key = 0, *langtag = 0, *transkey = 0; char *key = 0, *langtag = 0, *transkey = 0;
ucvector decoded; 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*/ 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 */ 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*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ /*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); data = lodepng_chunk_data_const(chunk);
unknown = 0;
/*IDAT chunk, containing compressed image data*/ /*IDAT chunk, containing compressed image data*/
if(lodepng_chunk_type_equals(chunk, "IDAT")) 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); state->error = readChunk_pHYs(&state->info_png, data, chunkLength);
if(state->error) break; 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*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
else /*it's not an implemented chunk type, so ignore it: skip over the data*/ 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; 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*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, 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; 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*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
unsigned lodepng_encode(unsigned char** out, size_t* outsize, 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); 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 (!state->error)
{ {
if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) 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]); state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]);
if(state->error) break; 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*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
/*PLTE*/ /*PLTE*/
if(info.color.colortype == LCT_PALETTE) 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 93: return "zero width or height is invalid";
case 94: return "header chunk must have a size of 13 bytes"; case 94: return "header chunk must have a size of 13 bytes";
case 95: return "integer overflow with combined idat chunk size"; 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"; return "unknown error code";
} }

View File

@ -1,5 +1,5 @@
/* /*
LodePNG version 20180611 LodePNG version 20180809
Copyright (c) 2005-2018 Lode Vandevenne Copyright (c) 2005-2018 Lode Vandevenne
@ -255,7 +255,7 @@ const char* lodepng_error_text(unsigned code);
typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; typedef struct LodePNGDecompressSettings LodePNGDecompressSettings;
struct 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*/ 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)*/ /*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 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. 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 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. 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)*/ unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/
/* /*
unknown chunks Color profile related chunks: gAMA, cHRM, sRGB, iCPP
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 LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color
The 3 buffers are the unknown chunks between certain critical chunks: profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please
0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND 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 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. 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); unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source);
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS #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*/ 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, 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*/ 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*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
/* /*
@ -521,10 +604,14 @@ typedef struct LodePNGDecoderSettings
{ {
LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ 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_crc; /*ignore CRC checksums*/
unsigned ignore_critical; /*ignore unknown critical chunks*/ 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 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*/ 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 LFS_PREDEFINED
} LodePNGFilterStrategy; } LodePNGFilterStrategy;
/*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding. /*Gives characteristics about the integer RGBA colors of the image (count, alpha channel usage, bit depth, ...),
Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ 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 typedef struct LodePNGColorProfile
{ {
unsigned colored; /*not greyscale*/ unsigned colored; /*not greyscale*/
@ -575,7 +665,9 @@ typedef struct LodePNGColorProfile
void lodepng_color_profile_init(LodePNGColorProfile* profile); 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, unsigned lodepng_get_color_profile(LodePNGColorProfile* profile,
const unsigned char* image, unsigned w, unsigned h, const unsigned char* image, unsigned w, unsigned h,
const LodePNGColorMode* mode_in); 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 [.] 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 [.] check compatibility with various compilers - done but needs to be redone for every newer version
[X] converting color to 16-bit per channel types [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 [ ] make sure encoder generates no chunks with size > (2^31)-1
[ ] partial decoding (stream processing) [ ] partial decoding (stream processing)
[X] let the "isFullyOpaque" function check color keys and transparent palettes too [X] let the "isFullyOpaque" function check color keys and transparent palettes too
[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" [X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl"
[ ] don't stop decoding on errors like 69, 57, 58 (make warnings) [ ] 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, ... [ ] 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 [ ] 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 provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ...
[ ] allow user to give data (void*) to custom allocator [ ] 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 (!) Some changes aren't backwards compatible. Those are indicated with a (!)
symbol. 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 *) 11 jun 2018: less restrictive check for pixel size integer overflow
*) 14 jan 2018: allow optionally ignoring a few more recoverable errors *) 14 jan 2018: allow optionally ignoring a few more recoverable errors
*) 17 sep 2017: fix memory leak for some encoder input error cases *) 17 sep 2017: fix memory leak for some encoder input error cases