1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-12-23 01:59:43 +01:00

Update LodePNG to 20220109

This updates LodePNG to the commit pushed on January 9, 2022.
This commit is contained in:
Misa 2022-02-11 11:54:18 -08:00
parent ed4d3d0fa8
commit 38d25c0850
2 changed files with 283 additions and 126 deletions

View file

@ -1,7 +1,7 @@
/* /*
LodePNG version 20200306 LodePNG version 20220109
Copyright (c) 2005-2020 Lode Vandevenne Copyright (c) 2005-2022 Lode Vandevenne
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -44,7 +44,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 = "20200306"; const char* LODEPNG_VERSION_STRING = "20220109";
/* /*
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
@ -267,7 +267,7 @@ typedef struct ucvector {
} ucvector; } ucvector;
/*returns 1 if success, 0 if failure ==> nothing done*/ /*returns 1 if success, 0 if failure ==> nothing done*/
static unsigned ucvector_resize(ucvector* p, size_t size) { static unsigned ucvector_reserve(ucvector* p, size_t size) {
if(size > p->allocsize) { if(size > p->allocsize) {
size_t newsize = size + (p->allocsize >> 1u); size_t newsize = size + (p->allocsize >> 1u);
void* data = lodepng_realloc(p->data, newsize); void* data = lodepng_realloc(p->data, newsize);
@ -277,10 +277,15 @@ static unsigned ucvector_resize(ucvector* p, size_t size) {
} }
else return 0; /*error: not enough memory*/ else return 0; /*error: not enough memory*/
} }
p->size = size;
return 1; /*success*/ return 1; /*success*/
} }
/*returns 1 if success, 0 if failure ==> nothing done*/
static unsigned ucvector_resize(ucvector* p, size_t size) {
p->size = size;
return ucvector_reserve(p, size);
}
static ucvector ucvector_init(unsigned char* buffer, size_t size) { static ucvector ucvector_init(unsigned char* buffer, size_t size) {
ucvector v; ucvector v;
v.data = buffer; v.data = buffer;
@ -299,6 +304,7 @@ static void string_cleanup(char** out) {
*out = NULL; *out = NULL;
} }
/*also appends null termination character*/
static char* alloc_string_sized(const char* in, size_t insize) { static char* alloc_string_sized(const char* in, size_t insize) {
char* out = (char*)lodepng_malloc(insize + 1); char* out = (char*)lodepng_malloc(insize + 1);
if(out) { if(out) {
@ -479,71 +485,62 @@ static unsigned LodePNGBitReader_init(LodePNGBitReader* reader, const unsigned c
ensureBits functions: ensureBits functions:
Ensures the reader can at least read nbits bits in one or more readBits calls, Ensures the reader can at least read nbits bits in one or more readBits calls,
safely even if not enough bits are available. safely even if not enough bits are available.
Returns 1 if there are enough bits available, 0 if not. The nbits parameter is unused but is given for documentation purposes, error
checking for amount of bits must be done beforehand.
*/ */
/*See ensureBits documentation above. This one ensures exactly 1 bit */
/*static unsigned ensureBits1(LodePNGBitReader* reader) {
if(reader->bp >= reader->bitsize) return 0;
reader->buffer = (unsigned)reader->data[reader->bp >> 3u] >> (reader->bp & 7u);
return 1;
}*/
/*See ensureBits documentation above. This one ensures up to 9 bits */ /*See ensureBits documentation above. This one ensures up to 9 bits */
static unsigned ensureBits9(LodePNGBitReader* reader, size_t nbits) { static LODEPNG_INLINE void ensureBits9(LodePNGBitReader* reader, size_t nbits) {
size_t start = reader->bp >> 3u; size_t start = reader->bp >> 3u;
size_t size = reader->size; size_t size = reader->size;
if(start + 1u < size) { if(start + 1u < size) {
reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u); reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u);
reader->buffer >>= (reader->bp & 7u); reader->buffer >>= (reader->bp & 7u);
return 1;
} else { } else {
reader->buffer = 0; reader->buffer = 0;
if(start + 0u < size) reader->buffer |= reader->data[start + 0]; if(start + 0u < size) reader->buffer = reader->data[start + 0];
reader->buffer >>= (reader->bp & 7u); reader->buffer >>= (reader->bp & 7u);
return reader->bp + nbits <= reader->bitsize;
} }
(void)nbits;
} }
/*See ensureBits documentation above. This one ensures up to 17 bits */ /*See ensureBits documentation above. This one ensures up to 17 bits */
static unsigned ensureBits17(LodePNGBitReader* reader, size_t nbits) { static LODEPNG_INLINE void ensureBits17(LodePNGBitReader* reader, size_t nbits) {
size_t start = reader->bp >> 3u; size_t start = reader->bp >> 3u;
size_t size = reader->size; size_t size = reader->size;
if(start + 2u < size) { if(start + 2u < size) {
reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) |
((unsigned)reader->data[start + 2] << 16u); ((unsigned)reader->data[start + 2] << 16u);
reader->buffer >>= (reader->bp & 7u); reader->buffer >>= (reader->bp & 7u);
return 1;
} else { } else {
reader->buffer = 0; reader->buffer = 0;
if(start + 0u < size) reader->buffer |= reader->data[start + 0]; if(start + 0u < size) reader->buffer |= reader->data[start + 0];
if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
reader->buffer >>= (reader->bp & 7u); reader->buffer >>= (reader->bp & 7u);
return reader->bp + nbits <= reader->bitsize;
} }
(void)nbits;
} }
/*See ensureBits documentation above. This one ensures up to 25 bits */ /*See ensureBits documentation above. This one ensures up to 25 bits */
static LODEPNG_INLINE unsigned ensureBits25(LodePNGBitReader* reader, size_t nbits) { static LODEPNG_INLINE void ensureBits25(LodePNGBitReader* reader, size_t nbits) {
size_t start = reader->bp >> 3u; size_t start = reader->bp >> 3u;
size_t size = reader->size; size_t size = reader->size;
if(start + 3u < size) { if(start + 3u < size) {
reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) |
((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
reader->buffer >>= (reader->bp & 7u); reader->buffer >>= (reader->bp & 7u);
return 1;
} else { } else {
reader->buffer = 0; reader->buffer = 0;
if(start + 0u < size) reader->buffer |= reader->data[start + 0]; if(start + 0u < size) reader->buffer |= reader->data[start + 0];
if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
reader->buffer >>= (reader->bp & 7u); reader->buffer >>= (reader->bp & 7u);
return reader->bp + nbits <= reader->bitsize;
} }
(void)nbits;
} }
/*See ensureBits documentation above. This one ensures up to 32 bits */ /*See ensureBits documentation above. This one ensures up to 32 bits */
static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbits) { static LODEPNG_INLINE void ensureBits32(LodePNGBitReader* reader, size_t nbits) {
size_t start = reader->bp >> 3u; size_t start = reader->bp >> 3u;
size_t size = reader->size; size_t size = reader->size;
if(start + 4u < size) { if(start + 4u < size) {
@ -551,7 +548,6 @@ static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbi
((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
reader->buffer >>= (reader->bp & 7u); reader->buffer >>= (reader->bp & 7u);
reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u))); reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u)));
return 1;
} else { } else {
reader->buffer = 0; reader->buffer = 0;
if(start + 0u < size) reader->buffer |= reader->data[start + 0]; if(start + 0u < size) reader->buffer |= reader->data[start + 0];
@ -559,48 +555,28 @@ static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbi
if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
if(start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u); if(start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u);
reader->buffer >>= (reader->bp & 7u); reader->buffer >>= (reader->bp & 7u);
return reader->bp + nbits <= reader->bitsize;
} }
(void)nbits;
} }
/* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */ /* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */
static unsigned peekBits(LodePNGBitReader* reader, size_t nbits) { static LODEPNG_INLINE unsigned peekBits(LodePNGBitReader* reader, size_t nbits) {
/* The shift allows nbits to be only up to 31. */ /* The shift allows nbits to be only up to 31. */
return reader->buffer & ((1u << nbits) - 1u); return reader->buffer & ((1u << nbits) - 1u);
} }
/* Must have enough bits available with ensureBits */ /* Must have enough bits available with ensureBits */
static void advanceBits(LodePNGBitReader* reader, size_t nbits) { static LODEPNG_INLINE void advanceBits(LodePNGBitReader* reader, size_t nbits) {
reader->buffer >>= nbits; reader->buffer >>= nbits;
reader->bp += nbits; reader->bp += nbits;
} }
/* Must have enough bits available with ensureBits */ /* Must have enough bits available with ensureBits */
static unsigned readBits(LodePNGBitReader* reader, size_t nbits) { static LODEPNG_INLINE unsigned readBits(LodePNGBitReader* reader, size_t nbits) {
unsigned result = peekBits(reader, nbits); unsigned result = peekBits(reader, nbits);
advanceBits(reader, nbits); advanceBits(reader, nbits);
return result; return result;
} }
/* Public for testing only. steps and result must have numsteps values. */
unsigned lode_png_test_bitreader(const unsigned char* data, size_t size,
size_t numsteps, const size_t* steps, unsigned* result) {
size_t i;
LodePNGBitReader reader;
unsigned error = LodePNGBitReader_init(&reader, data, size);
if(error) return 0;
for(i = 0; i < numsteps; i++) {
size_t step = steps[i];
unsigned ok;
if(step > 25) ok = ensureBits32(&reader, step);
else if(step > 17) ok = ensureBits25(&reader, step);
else if(step > 9) ok = ensureBits17(&reader, step);
else ok = ensureBits9(&reader, step);
if(!ok) return 0;
result[i] = readBits(&reader, step);
}
return 1;
}
#endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_DECODER*/
static unsigned reverseBits(unsigned bits, unsigned num) { static unsigned reverseBits(unsigned bits, unsigned num) {
@ -1102,11 +1078,10 @@ static unsigned huffmanDecodeSymbol(LodePNGBitReader* reader, const HuffmanTree*
advanceBits(reader, l); advanceBits(reader, l);
return value; return value;
} else { } else {
unsigned index2;
advanceBits(reader, FIRSTBITS); advanceBits(reader, FIRSTBITS);
index2 = value + peekBits(reader, l - FIRSTBITS); value += peekBits(reader, l - FIRSTBITS);
advanceBits(reader, codetree->table_len[index2] - FIRSTBITS); advanceBits(reader, codetree->table_len[value] - FIRSTBITS);
return codetree->table_value[index2]; return codetree->table_value[value];
} }
} }
#endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_DECODER*/
@ -1139,7 +1114,8 @@ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d,
unsigned* bitlen_cl = 0; unsigned* bitlen_cl = 0;
HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/
if(!ensureBits17(reader, 14)) return 49; /*error: the bit pointer is or will go past the memory*/ if(reader->bitsize - reader->bp < 14) return 49; /*error: the bit pointer is or will go past the memory*/
ensureBits17(reader, 14);
/*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/
HLIT = readBits(reader, 5) + 257; HLIT = readBits(reader, 5) + 257;
@ -1260,10 +1236,14 @@ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d,
/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/ /*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/
static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader, static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
unsigned btype) { unsigned btype, size_t max_output_size) {
unsigned error = 0; unsigned error = 0;
HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/
HuffmanTree tree_d; /*the huffman tree for distance codes*/ HuffmanTree tree_d; /*the huffman tree for distance codes*/
const size_t reserved_size = 260; /* must be at least 258 for max length, and a few extra for adding a few extra literals */
int done = 0;
if(!ucvector_reserve(out, out->size + reserved_size)) return 83; /*alloc fail*/
HuffmanTree_init(&tree_ll); HuffmanTree_init(&tree_ll);
HuffmanTree_init(&tree_d); HuffmanTree_init(&tree_d);
@ -1271,14 +1251,21 @@ static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
if(btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d); if(btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d);
else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader); else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader);
while(!error) /*decode all symbols until end reached, breaks at end code*/ {
while(!error && !done) /*decode all symbols until end reached, breaks at end code*/ {
/*code_ll is literal, length or end code*/ /*code_ll is literal, length or end code*/
unsigned code_ll; unsigned code_ll;
ensureBits25(reader, 20); /* up to 15 for the huffman symbol, up to 5 for the length extra bits */ /* ensure enough bits for 2 huffman code reads (15 bits each): if the first is a literal, a second literal is read at once. This
appears to be slightly faster, than ensuring 20 bits here for 1 huffman symbol and the potential 5 extra bits for the length symbol.*/
ensureBits32(reader, 30);
code_ll = huffmanDecodeSymbol(reader, &tree_ll); code_ll = huffmanDecodeSymbol(reader, &tree_ll);
if(code_ll <= 255) {
/*slightly faster code path if multiple literals in a row*/
out->data[out->size++] = (unsigned char)code_ll;
code_ll = huffmanDecodeSymbol(reader, &tree_ll);
}
if(code_ll <= 255) /*literal symbol*/ { if(code_ll <= 255) /*literal symbol*/ {
if(!ucvector_resize(out, out->size + 1)) ERROR_BREAK(83 /*alloc fail*/); out->data[out->size++] = (unsigned char)code_ll;
out->data[out->size - 1] = (unsigned char)code_ll;
} else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ { } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ {
unsigned code_d, distance; unsigned code_d, distance;
unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/
@ -1291,6 +1278,7 @@ static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX];
if(numextrabits_l != 0) { if(numextrabits_l != 0) {
/* bits already ensured above */ /* bits already ensured above */
ensureBits25(reader, 5);
length += readBits(reader, numextrabits_l); length += readBits(reader, numextrabits_l);
} }
@ -1318,7 +1306,7 @@ static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ if(distance > start) ERROR_BREAK(52); /*too long backward distance*/
backward = start - distance; backward = start - distance;
if(!ucvector_resize(out, out->size + length)) ERROR_BREAK(83 /*alloc fail*/); out->size += length;
if(distance < length) { if(distance < length) {
size_t forward; size_t forward;
lodepng_memcpy(out->data + start, out->data + backward, distance); lodepng_memcpy(out->data + start, out->data + backward, distance);
@ -1330,10 +1318,13 @@ static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
lodepng_memcpy(out->data + start, out->data + backward, length); lodepng_memcpy(out->data + start, out->data + backward, length);
} }
} else if(code_ll == 256) { } else if(code_ll == 256) {
break; /*end code, break the loop*/ done = 1; /*end code, finish the loop*/
} else /*if(code_ll == INVALIDSYMBOL)*/ { } else /*if(code_ll == INVALIDSYMBOL)*/ {
ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
} }
if(out->allocsize - out->size < reserved_size) {
if(!ucvector_reserve(out, out->size + reserved_size)) ERROR_BREAK(83); /*alloc fail*/
}
/*check if any of the ensureBits above went out of bounds*/ /*check if any of the ensureBits above went out of bounds*/
if(reader->bp > reader->bitsize) { if(reader->bp > reader->bitsize) {
/*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
@ -1341,6 +1332,9 @@ static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
/* TODO: revise error codes 10,11,50: the above comment is no longer valid */ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
ERROR_BREAK(51); /*error, bit pointer jumps past memory*/ ERROR_BREAK(51); /*error, bit pointer jumps past memory*/
} }
if(max_output_size && out->size > max_output_size) {
ERROR_BREAK(109); /*error, larger than max size*/
}
} }
HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_ll);
@ -1392,15 +1386,16 @@ static unsigned lodepng_inflatev(ucvector* out,
while(!BFINAL) { while(!BFINAL) {
unsigned BTYPE; unsigned BTYPE;
if(!ensureBits9(&reader, 3)) return 52; /*error, bit pointer will jump past memory*/ if(reader.bitsize - reader.bp < 3) return 52; /*error, bit pointer will jump past memory*/
ensureBits9(&reader, 3);
BFINAL = readBits(&reader, 1); BFINAL = readBits(&reader, 1);
BTYPE = readBits(&reader, 2); BTYPE = readBits(&reader, 2);
if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ if(BTYPE == 3) return 20; /*error: invalid BTYPE*/
else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/ else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/
else error = inflateHuffmanBlock(out, &reader, BTYPE); /*compression, BTYPE 01 or 10*/ else error = inflateHuffmanBlock(out, &reader, BTYPE, settings->max_output_size); /*compression, BTYPE 01 or 10*/
if(!error && settings->max_output_size && out->size > settings->max_output_size) error = 109;
if(error) return error; if(error) break;
} }
return error; return error;
@ -1421,6 +1416,12 @@ static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize,
if(settings->custom_inflate) { if(settings->custom_inflate) {
unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings); unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings);
out->allocsize = out->size; out->allocsize = out->size;
if(error) {
/*the custom inflate is allowed to have its own error codes, however, we translate it to code 110*/
error = 110;
/*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/
if(settings->max_output_size && out->size > settings->max_output_size) error = 109;
}
return error; return error;
} else { } else {
return lodepng_inflatev(out, in, insize, settings); return lodepng_inflatev(out, in, insize, settings);
@ -2116,7 +2117,9 @@ static unsigned deflate(unsigned char** out, size_t* outsize,
const unsigned char* in, size_t insize, const unsigned char* in, size_t insize,
const LodePNGCompressSettings* settings) { const LodePNGCompressSettings* settings) {
if(settings->custom_deflate) { if(settings->custom_deflate) {
return settings->custom_deflate(out, outsize, in, insize, settings); unsigned error = settings->custom_deflate(out, outsize, in, insize, settings);
/*the custom deflate is allowed to have its own error codes, however, we translate it to code 111*/
return error ? 111 : 0;
} else { } else {
return lodepng_deflate(out, outsize, in, insize, settings); return lodepng_deflate(out, outsize, in, insize, settings);
} }
@ -2213,10 +2216,16 @@ unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const uns
/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */ /*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */
static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size,
const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) {
unsigned error;
if(settings->custom_zlib) { if(settings->custom_zlib) {
return settings->custom_zlib(out, outsize, in, insize, settings); error = settings->custom_zlib(out, outsize, in, insize, settings);
if(error) {
/*the custom zlib is allowed to have its own error codes, however, we translate it to code 110*/
error = 110;
/*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/
if(settings->max_output_size && *outsize > settings->max_output_size) error = 109;
}
} else { } else {
unsigned error;
ucvector v = ucvector_init(*out, *outsize); ucvector v = ucvector_init(*out, *outsize);
if(expected_size) { if(expected_size) {
/*reserve the memory to avoid intermediate reallocations*/ /*reserve the memory to avoid intermediate reallocations*/
@ -2226,8 +2235,8 @@ static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t exp
error = lodepng_zlib_decompressv(&v, in, insize, settings); error = lodepng_zlib_decompressv(&v, in, insize, settings);
*out = v.data; *out = v.data;
*outsize = v.size; *outsize = v.size;
return error;
} }
return error;
} }
#endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_DECODER*/
@ -2275,7 +2284,9 @@ unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsig
static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
size_t insize, const LodePNGCompressSettings* settings) { size_t insize, const LodePNGCompressSettings* settings) {
if(settings->custom_zlib) { if(settings->custom_zlib) {
return settings->custom_zlib(out, outsize, in, insize, settings); unsigned error = settings->custom_zlib(out, outsize, in, insize, settings);
/*the custom zlib is allowed to have its own error codes, however, we translate it to code 111*/
return error ? 111 : 0;
} else { } else {
return lodepng_zlib_compress(out, outsize, in, insize, settings); return lodepng_zlib_compress(out, outsize, in, insize, settings);
} }
@ -2334,13 +2345,14 @@ const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT
void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) { void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) {
settings->ignore_adler32 = 0; settings->ignore_adler32 = 0;
settings->ignore_nlen = 0; settings->ignore_nlen = 0;
settings->max_output_size = 0;
settings->custom_zlib = 0; settings->custom_zlib = 0;
settings->custom_inflate = 0; settings->custom_inflate = 0;
settings->custom_context = 0; settings->custom_context = 0;
} }
const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0}; const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0, 0};
#endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_DECODER*/
@ -2872,8 +2884,8 @@ static void LodePNGText_cleanup(LodePNGInfo* info) {
static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
size_t i = 0; size_t i = 0;
dest->text_keys = 0; dest->text_keys = NULL;
dest->text_strings = 0; dest->text_strings = NULL;
dest->text_num = 0; dest->text_num = 0;
for(i = 0; i != source->text_num; ++i) { for(i = 0; i != source->text_num; ++i) {
CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i]));
@ -2932,10 +2944,10 @@ static void LodePNGIText_cleanup(LodePNGInfo* info) {
static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
size_t i = 0; size_t i = 0;
dest->itext_keys = 0; dest->itext_keys = NULL;
dest->itext_langtags = 0; dest->itext_langtags = NULL;
dest->itext_transkeys = 0; dest->itext_transkeys = NULL;
dest->itext_strings = 0; dest->itext_strings = NULL;
dest->itext_num = 0; dest->itext_num = 0;
for(i = 0; i != source->itext_num; ++i) { for(i = 0; i != source->itext_num; ++i) {
CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i],
@ -4093,10 +4105,12 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
case 0: case 0:
for(i = 0; i != length; ++i) recon[i] = scanline[i]; for(i = 0; i != length; ++i) recon[i] = scanline[i];
break; break;
case 1: case 1: {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; for(i = bytewidth; i != length; ++i, ++j) recon[i] = scanline[i] + recon[j];
break; break;
}
case 2: case 2:
if(precon) { if(precon) {
for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i];
@ -4106,24 +4120,56 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
break; break;
case 3: case 3:
if(precon) { if(precon) {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u); for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u);
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u); /* Unroll independent paths of this predictor. A 6x and 8x version is also possible but that adds
too much code. Whether this speeds up anything depends on compiler and settings. */
if(bytewidth >= 4) {
for(; i + 3 < length; i += 4, j += 4) {
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3];
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
recon[i + 2] = s2 + ((r2 + p2) >> 1u);
recon[i + 3] = s3 + ((r3 + p3) >> 1u);
}
} else if(bytewidth >= 3) {
for(; i + 2 < length; i += 3, j += 3) {
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2];
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
recon[i + 2] = s2 + ((r2 + p2) >> 1u);
}
} else if(bytewidth >= 2) {
for(; i + 1 < length; i += 2, j += 2) {
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1];
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
}
}
for(; i != length; ++i, ++j) recon[i] = scanline[i] + ((recon[j] + precon[i]) >> 1u);
} else { } else {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u); for(i = bytewidth; i != length; ++i, ++j) recon[i] = scanline[i] + (recon[j] >> 1u);
} }
break; break;
case 4: case 4:
if(precon) { if(precon) {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) { for(i = 0; i != bytewidth; ++i) {
recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/
} }
/* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that /* Unroll independent paths of the paeth predictor. A 6x and 8x version is also possible but that
adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */ adds too much code. Whether this speeds up anything depends on compiler and settings. */
if(bytewidth >= 4) { if(bytewidth >= 4) {
for(; i + 3 < length; i += 4) { for(; i + 3 < length; i += 4, j += 4) {
size_t j = i - bytewidth;
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3]; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3];
@ -4134,8 +4180,7 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
recon[i + 3] = s3 + paethPredictor(r3, p3, q3); recon[i + 3] = s3 + paethPredictor(r3, p3, q3);
} }
} else if(bytewidth >= 3) { } else if(bytewidth >= 3) {
for(; i + 2 < length; i += 3) { for(; i + 2 < length; i += 3, j += 3) {
size_t j = i - bytewidth;
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2]; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2];
@ -4145,8 +4190,7 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
recon[i + 2] = s2 + paethPredictor(r2, p2, q2); recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
} }
} else if(bytewidth >= 2) { } else if(bytewidth >= 2) {
for(; i + 1 < length; i += 2) { for(; i + 1 < length; i += 2, j += 2) {
size_t j = i - bytewidth;
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1]; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1];
@ -4156,16 +4200,17 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
} }
} }
for(; i != length; ++i) { for(; i != length; ++i, ++j) {
recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[j]));
} }
} else { } else {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) { for(i = 0; i != bytewidth; ++i) {
recon[i] = scanline[i]; recon[i] = scanline[i];
} }
for(i = bytewidth; i < length; ++i) { for(i = bytewidth; i != length; ++i, ++j) {
/*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/
recon[i] = (scanline[i] + recon[i - bytewidth]); recon[i] = (scanline[i] + recon[j]);
} }
} }
break; break;
@ -4447,10 +4492,13 @@ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, siz
} }
/*compressed text chunk (zTXt)*/ /*compressed text chunk (zTXt)*/
static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
const unsigned char* data, size_t chunkLength) { const unsigned char* data, size_t chunkLength) {
unsigned error = 0; unsigned error = 0;
/*copy the object to change parameters in it*/
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
unsigned length, string2_begin; unsigned length, string2_begin;
char *key = 0; char *key = 0;
unsigned char* str = 0; unsigned char* str = 0;
@ -4473,12 +4521,14 @@ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSetting
if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/
length = (unsigned)chunkLength - string2_begin; length = (unsigned)chunkLength - string2_begin;
zlibsettings.max_output_size = decoder->max_text_size;
/*will fail if zlib error, e.g. if length is too small*/ /*will fail if zlib error, e.g. if length is too small*/
error = zlib_decompress(&str, &size, 0, &data[string2_begin], error = zlib_decompress(&str, &size, 0, &data[string2_begin],
length, zlibsettings); length, &zlibsettings);
/*error: compressed text larger than decoder->max_text_size*/
if(error && size > zlibsettings.max_output_size) error = 112;
if(error) break; if(error) break;
error = lodepng_add_text_sized(info, key, (char*)str, size); error = lodepng_add_text_sized(info, key, (char*)str, size);
break; break;
} }
@ -4489,11 +4539,14 @@ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSetting
} }
/*international text chunk (iTXt)*/ /*international text chunk (iTXt)*/
static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
const unsigned char* data, size_t chunkLength) { const unsigned char* data, size_t chunkLength) {
unsigned error = 0; unsigned error = 0;
unsigned i; unsigned i;
/*copy the object to change parameters in it*/
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
unsigned length, begin, compressed; unsigned length, begin, compressed;
char *key = 0, *langtag = 0, *transkey = 0; char *key = 0, *langtag = 0, *transkey = 0;
@ -4550,9 +4603,12 @@ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSetting
if(compressed) { if(compressed) {
unsigned char* str = 0; unsigned char* str = 0;
size_t size = 0; size_t size = 0;
zlibsettings.max_output_size = decoder->max_text_size;
/*will fail if zlib error, e.g. if length is too small*/ /*will fail if zlib error, e.g. if length is too small*/
error = zlib_decompress(&str, &size, 0, &data[begin], error = zlib_decompress(&str, &size, 0, &data[begin],
length, zlibsettings); length, &zlibsettings);
/*error: compressed text larger than decoder->max_text_size*/
if(error && size > zlibsettings.max_output_size) error = 112;
if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size); if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size);
lodepng_free(str); lodepng_free(str);
} else { } else {
@ -4628,11 +4684,13 @@ static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, siz
return 0; /* OK */ return 0; /* OK */
} }
static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
const unsigned char* data, size_t chunkLength) { const unsigned char* data, size_t chunkLength) {
unsigned error = 0; unsigned error = 0;
unsigned i; unsigned i;
size_t size = 0; size_t size = 0;
/*copy the object to change parameters in it*/
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
unsigned length, string2_begin; unsigned length, string2_begin;
@ -4655,9 +4713,12 @@ static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSetting
if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/ if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/
length = (unsigned)chunkLength - string2_begin; length = (unsigned)chunkLength - string2_begin;
zlibsettings.max_output_size = decoder->max_icc_size;
error = zlib_decompress(&info->iccp_profile, &size, 0, error = zlib_decompress(&info->iccp_profile, &size, 0,
&data[string2_begin], &data[string2_begin],
length, zlibsettings); length, &zlibsettings);
/*error: ICC profile larger than decoder->max_icc_size*/
if(error && size > zlibsettings.max_output_size) error = 113;
info->iccp_profile_size = size; info->iccp_profile_size = size;
if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/ if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/
return error; return error;
@ -4688,9 +4749,9 @@ unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
} else if(lodepng_chunk_type_equals(chunk, "tEXt")) { } else if(lodepng_chunk_type_equals(chunk, "tEXt")) {
error = readChunk_tEXt(&state->info_png, data, chunkLength); error = readChunk_tEXt(&state->info_png, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "zTXt")) { } else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "iTXt")) { } else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "tIME")) { } else if(lodepng_chunk_type_equals(chunk, "tIME")) {
error = readChunk_tIME(&state->info_png, data, chunkLength); error = readChunk_tIME(&state->info_png, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "pHYs")) { } else if(lodepng_chunk_type_equals(chunk, "pHYs")) {
@ -4702,7 +4763,7 @@ unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
} else if(lodepng_chunk_type_equals(chunk, "sRGB")) { } else if(lodepng_chunk_type_equals(chunk, "sRGB")) {
error = readChunk_sRGB(&state->info_png, data, chunkLength); error = readChunk_sRGB(&state->info_png, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "iCCP")) { } else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength);
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} else { } else {
/* unhandled chunk is ok (is not an error) */ /* unhandled chunk is ok (is not an error) */
@ -4820,13 +4881,13 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
} else if(lodepng_chunk_type_equals(chunk, "zTXt")) { } else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
/*compressed text chunk (zTXt)*/ /*compressed text chunk (zTXt)*/
if(state->decoder.read_text_chunks) { if(state->decoder.read_text_chunks) {
state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); state->error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength);
if(state->error) break; if(state->error) break;
} }
} else if(lodepng_chunk_type_equals(chunk, "iTXt")) { } else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
/*international text chunk (iTXt)*/ /*international text chunk (iTXt)*/
if(state->decoder.read_text_chunks) { if(state->decoder.read_text_chunks) {
state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); state->error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength);
if(state->error) break; if(state->error) break;
} }
} else if(lodepng_chunk_type_equals(chunk, "tIME")) { } else if(lodepng_chunk_type_equals(chunk, "tIME")) {
@ -4845,7 +4906,7 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
state->error = readChunk_sRGB(&state->info_png, data, chunkLength); state->error = readChunk_sRGB(&state->info_png, data, chunkLength);
if(state->error) break; if(state->error) break;
} else if(lodepng_chunk_type_equals(chunk, "iCCP")) { } else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
state->error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); state->error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength);
if(state->error) break; 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*/ {
@ -4871,7 +4932,7 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize); if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize);
} }
if(state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) { if(!state->error && state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) {
state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */ state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */
} }
@ -4955,6 +5016,11 @@ unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, co
lodepng_state_init(&state); lodepng_state_init(&state);
state.info_raw.colortype = colortype; state.info_raw.colortype = colortype;
state.info_raw.bitdepth = bitdepth; state.info_raw.bitdepth = bitdepth;
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
/*disable reading things that this function doesn't output*/
state.decoder.read_text_chunks = 0;
state.decoder.remember_unknown_chunks = 0;
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
error = lodepng_decode(out, w, h, &state, in, insize); error = lodepng_decode(out, w, h, &state, in, insize);
lodepng_state_cleanup(&state); lodepng_state_cleanup(&state);
return error; return error;
@ -4997,6 +5063,8 @@ void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) {
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
settings->read_text_chunks = 1; settings->read_text_chunks = 1;
settings->remember_unknown_chunks = 0; settings->remember_unknown_chunks = 0;
settings->max_text_size = 16777216;
settings->max_icc_size = 16777216; /* 16MB is much more than enough for any reasonable ICC profile */
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
settings->ignore_crc = 0; settings->ignore_crc = 0;
settings->ignore_critical = 0; settings->ignore_critical = 0;
@ -5871,11 +5939,15 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize,
if(!state->error) { if(!state->error) {
state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); 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); if(!state->error) {
state->error = preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder);
}
lodepng_free(converted); lodepng_free(converted);
if(state->error) goto cleanup; if(state->error) goto cleanup;
} else {
state->error = preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder);
if(state->error) goto cleanup;
} }
else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder);
/* output all PNG chunks */ { /* output all PNG chunks */ {
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
@ -6200,6 +6272,16 @@ const char* lodepng_error_text(unsigned code) {
case 106: return "PNG file must have PLTE chunk if color type is palette"; case 106: return "PNG file must have PLTE chunk if color type is palette";
case 107: return "color convert from palette mode requested without setting the palette data in it"; case 107: return "color convert from palette mode requested without setting the palette data in it";
case 108: return "tried to add more than 256 values to a palette"; case 108: return "tried to add more than 256 values to a palette";
/*this limit can be configured in LodePNGDecompressSettings*/
case 109: return "tried to decompress zlib or deflate data larger than desired max_output_size";
case 110: return "custom zlib or inflate decompression failed";
case 111: return "custom zlib or deflate compression failed";
/*max text size limit can be configured in LodePNGDecoderSettings. This error prevents
unreasonable memory consumption when decoding due to impossibly large text sizes.*/
case 112: return "compressed text unreasonably large";
/*max ICC size limit can be configured in LodePNGDecoderSettings. This error prevents
unreasonable memory consumption when decoding due to impossibly large ICC profile*/
case 113: return "ICC profile unreasonably large";
} }
return "unknown error code"; return "unknown error code";
} }

View file

@ -1,7 +1,7 @@
/* /*
LodePNG version 20200306 LodePNG version 20220109
Copyright (c) 2005-2020 Lode Vandevenne Copyright (c) 2005-2022 Lode Vandevenne
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -142,16 +142,24 @@ unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h,
/* /*
Load PNG from disk, from file with given name. Load PNG from disk, from file with given name.
Same as the other decode functions, but instead takes a filename as input. Same as the other decode functions, but instead takes a filename as input.
*/
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.*/
unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h,
const char* filename, const char* filename,
LodePNGColorType colortype, unsigned bitdepth); LodePNGColorType colortype, unsigned bitdepth);
/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/ /*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.*/
unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h,
const char* filename); const char* filename);
/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/ /*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.*/
unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h,
const char* filename); const char* filename);
#endif /*LODEPNG_COMPILE_DISK*/ #endif /*LODEPNG_COMPILE_DISK*/
@ -191,17 +199,26 @@ unsigned lodepng_encode24(unsigned char** out, size_t* outsize,
/* /*
Converts raw pixel data into a PNG file on disk. Converts raw pixel data into a PNG file on disk.
Same as the other encode functions, but instead takes a filename as output. Same as the other encode functions, but instead takes a filename as output.
NOTE: This overwrites existing files without warning! NOTE: This overwrites existing files without warning!
*/
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory.*/
unsigned lodepng_encode_file(const char* filename, unsigned lodepng_encode_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h, const unsigned char* image, unsigned w, unsigned h,
LodePNGColorType colortype, unsigned bitdepth); LodePNGColorType colortype, unsigned bitdepth);
/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/ /*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory.*/
unsigned lodepng_encode32_file(const char* filename, unsigned lodepng_encode32_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h); const unsigned char* image, unsigned w, unsigned h);
/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/ /*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory.*/
unsigned lodepng_encode24_file(const char* filename, unsigned lodepng_encode24_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h); const unsigned char* image, unsigned w, unsigned h);
#endif /*LODEPNG_COMPILE_DISK*/ #endif /*LODEPNG_COMPILE_DISK*/
@ -223,6 +240,9 @@ unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
/* /*
Converts PNG file from disk to raw pixel data in memory. Converts PNG file from disk to raw pixel data in memory.
Same as the other decode functions, but instead takes a filename as input. Same as the other decode functions, but instead takes a filename as input.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.
*/ */
unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
const std::string& filename, const std::string& filename,
@ -243,7 +263,11 @@ unsigned encode(std::vector<unsigned char>& out,
/* /*
Converts 32-bit RGBA raw pixel data into a PNG file on disk. Converts 32-bit RGBA raw pixel data into a PNG file on disk.
Same as the other encode functions, but instead takes a filename as output. Same as the other encode functions, but instead takes a filename as output.
NOTE: This overwrites existing files without warning! NOTE: This overwrites existing files without warning!
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.
*/ */
unsigned encode(const std::string& filename, unsigned encode(const std::string& filename,
const unsigned char* in, unsigned w, unsigned h, const unsigned char* in, unsigned w, unsigned h,
@ -270,12 +294,21 @@ struct LodePNGDecompressSettings {
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*/
unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/ unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
/*use custom zlib decoder instead of built in one (default: null)*/ /*Maximum decompressed size, beyond this the decoder may (and is encouraged to) stop decoding,
return an error, output a data size > max_output_size and all the data up to that point. This is
not hard limit nor a guarantee, but can prevent excessive memory usage. This setting is
ignored by the PNG decoder, but is used by the deflate/zlib decoder and can be used by custom ones.
Set to 0 to impose no limit (the default).*/
size_t max_output_size;
/*use custom zlib decoder instead of built in one (default: null).
Should return 0 if success, any non-0 if error (numeric value not exposed).*/
unsigned (*custom_zlib)(unsigned char**, size_t*, unsigned (*custom_zlib)(unsigned char**, size_t*,
const unsigned char*, size_t, const unsigned char*, size_t,
const LodePNGDecompressSettings*); const LodePNGDecompressSettings*);
/*use custom deflate decoder instead of built in one (default: null) /*use custom deflate decoder instead of built in one (default: null)
if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/ if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate).
Should return 0 if success, any non-0 if error (numeric value not exposed).*/
unsigned (*custom_inflate)(unsigned char**, size_t*, unsigned (*custom_inflate)(unsigned char**, size_t*,
const unsigned char*, size_t, const unsigned char*, size_t,
const LodePNGDecompressSettings*); const LodePNGDecompressSettings*);
@ -454,30 +487,36 @@ typedef struct LodePNGInfo {
unsigned background_b; /*blue component of suggested background color*/ unsigned background_b; /*blue component of suggested background color*/
/* /*
non-international text chunks (tEXt and zTXt) Non-international text chunks (tEXt and zTXt)
The char** arrays each contain num strings. The actual messages are in The char** arrays each contain num strings. The actual messages are in
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. All the string fields below including strings, keys, names and language tags are null terminated.
The PNG specification uses null characters for the keys, names and tags, and forbids null 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. 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 (plus the
discouraged to use a single line length longer than 79 characters for texts. additional null terminator). It's discouraged to use a single line length
longer than 79 characters for texts.
Don't allocate these text buffers yourself. Use the init/cleanup functions Don't allocate these text buffers yourself. Use the init/cleanup functions
correctly and use lodepng_add_text and lodepng_clear_text. correctly and use lodepng_add_text and lodepng_clear_text.
Standard text chunk keywords and strings are encoded using Latin-1.
*/ */
size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/
char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/
char** text_strings; /*the actual text*/ char** text_strings; /*the actual text*/
/* /*
international text chunks (iTXt) International text chunks (iTXt)
Similar to the non-international text chunks, but with additional strings Similar to the non-international text chunks, but with additional strings
"langtags" and "transkeys". "langtags" and "transkeys", and the following text encodings are used:
keys: Latin-1, langtags: ASCII, transkeys and strings: UTF-8.
keys must be 1-79 characters (plus the additional null terminator), the other
strings are any length.
*/ */
size_t itext_num; /*the amount of international texts in this PNG*/ size_t itext_num; /*the amount of international texts in this PNG*/
char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/
@ -639,8 +678,19 @@ typedef struct LodePNGDecoderSettings {
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/
/*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/
unsigned remember_unknown_chunks; unsigned remember_unknown_chunks;
/* maximum size for decompressed text chunks. If a text chunk's text is larger than this, an error is returned,
unless reading text chunks is disabled or this limit is set higher or disabled. Set to 0 to allow any size.
By default it is a value that prevents unreasonably large strings from hogging memory. */
size_t max_text_size;
/* maximum size for compressed ICC chunks. If the ICC profile is larger than this, an error will be returned. Set to
0 to allow any size. By default this is a value that prevents ICC profiles that would be much larger than any
legitimate profile could be to hog memory. */
size_t max_icc_size;
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} LodePNGDecoderSettings; } LodePNGDecoderSettings;
@ -950,6 +1000,9 @@ out: output parameter, contains pointer to loaded buffer.
outsize: output parameter, size of the allocated out buffer outsize: output parameter, size of the allocated out buffer
filename: the path to the file to load filename: the path to the file to load
return value: error code (0 means ok) return value: error code (0 means ok)
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.
*/ */
unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename); unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename);
@ -960,6 +1013,9 @@ buffer: the buffer to write
buffersize: size of the buffer to write buffersize: size of the buffer to write
filename: the path to the file to save to filename: the path to the file to save to
return value: error code (0 means ok) return value: error code (0 means ok)
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory
*/ */
unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename); unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename);
#endif /*LODEPNG_COMPILE_DISK*/ #endif /*LODEPNG_COMPILE_DISK*/
@ -1000,12 +1056,18 @@ unsigned encode(std::vector<unsigned char>& out,
/* /*
Load a file from disk into an std::vector. Load a file from disk into an std::vector.
return value: error code (0 means ok) return value: error code (0 means ok)
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory
*/ */
unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename); unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename);
/* /*
Save the binary data in an std::vector to a file on disk. The file is overwritten Save the binary data in an std::vector to a file on disk. The file is overwritten
without warning. without warning.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory
*/ */
unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename); unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename);
#endif /* LODEPNG_COMPILE_DISK */ #endif /* LODEPNG_COMPILE_DISK */
@ -1505,6 +1567,11 @@ of the error in English as a string.
Check the implementation of lodepng_error_text to see the meaning of each code. Check the implementation of lodepng_error_text to see the meaning of each code.
It is not recommended to use the numerical values to programmatically make
different decisions based on error types as the numbers are not guaranteed to
stay backwards compatible. They are for human consumption only. Programmatically
only 0 or non-0 matter.
8. chunks and PNG editing 8. chunks and PNG editing
------------------------- -------------------------
@ -1678,6 +1745,9 @@ try to fix it if the compiler is modern and standards compliant.
This decoder example shows the most basic usage of LodePNG. More complex This decoder example shows the most basic usage of LodePNG. More complex
examples can be found on the LodePNG website. examples can be found on the LodePNG website.
NOTE: these examples do not support wide-character filenames, you can use an
external method to handle such files and encode or decode in-memory
10.1. decoder C++ example 10.1. decoder C++ example
------------------------- -------------------------
@ -1775,6 +1845,11 @@ symbol.
Not all changes are listed here, the commit history in github lists more: Not all changes are listed here, the commit history in github lists more:
https://github.com/lvandeve/lodepng https://github.com/lvandeve/lodepng
*) 09 jan 2022: minor decoder speed improvements.
*) 27 jun 2021: added warnings that file reading/writing functions don't support
wide-character filenames (support for this is not planned, opening files is
not the core part of PNG decoding/decoding and is platform dependent).
*) 17 okt 2020: prevent decoding too large text/icc chunks by default.
*) 06 mar 2020: simplified some of the dynamic memory allocations. *) 06 mar 2020: simplified some of the dynamic memory allocations.
*) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct *) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct
overflow checks. overflow checks.
@ -1941,5 +2016,5 @@ Domain: gmail dot com.
Account: lode dot vandevenne. Account: lode dot vandevenne.
Copyright (c) 2005-2020 Lode Vandevenne Copyright (c) 2005-2022 Lode Vandevenne
*/ */