Skip to content

Commit 67536c5

Browse files
Allow multiple window sizes for zlib compression in PNG.
1 parent bf801de commit 67536c5

File tree

1 file changed

+69
-6
lines changed

1 file changed

+69
-6
lines changed

png.cpp

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2462,11 +2462,63 @@ PNG_CHUNK_FDAT* PNG_CHUNK_FDAT::generate() {
24622462

24632463

24642464

2465-
std::string compress_data(std::string data, int level) {
2465+
int ZEXPORT compress3(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level, int windowBits)
2466+
{
2467+
z_stream stream;
2468+
int err;
2469+
const uInt max = (uInt)-1;
2470+
uLong left;
2471+
2472+
left = *destLen;
2473+
*destLen = 0;
2474+
2475+
stream.zalloc = (alloc_func)0;
2476+
stream.zfree = (free_func)0;
2477+
stream.opaque = (voidpf)0;
2478+
2479+
err = deflateInit2(&stream, level, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY);
2480+
if (err != Z_OK) return err;
2481+
2482+
stream.next_out = dest;
2483+
stream.avail_out = 0;
2484+
stream.next_in = (z_const Bytef *)source;
2485+
stream.avail_in = 0;
2486+
2487+
do {
2488+
if (stream.avail_out == 0) {
2489+
stream.avail_out = left > (uLong)max ? max : (uInt)left;
2490+
left -= stream.avail_out;
2491+
}
2492+
if (stream.avail_in == 0) {
2493+
stream.avail_in = sourceLen > (uLong)max ? max : (uInt)sourceLen;
2494+
sourceLen -= stream.avail_in;
2495+
}
2496+
err = deflate(&stream, sourceLen ? Z_NO_FLUSH : Z_FINISH);
2497+
} while (err == Z_OK);
2498+
2499+
*destLen = stream.total_out;
2500+
deflateEnd(&stream);
2501+
return err == Z_STREAM_END ? Z_OK : err;
2502+
}
2503+
2504+
2505+
2506+
std::string compress_data(std::string data, int level, int windowBits) {
24662507
unsigned long data_len = data.size();
2467-
unsigned long comp_len = compressBound(data_len);
2508+
unsigned long comp_len = 2 * compressBound(data_len);
24682509
unsigned char* compressed = (unsigned char*) malloc(comp_len);
2469-
compress2(compressed, &comp_len, (const Bytef*) data.c_str(), data_len, level);
2510+
compress3(compressed, &comp_len, (const Bytef*) data.c_str(), data_len, level, windowBits);
2511+
if (windowBits == 8 && compressed[0] == 0x18) {
2512+
compressed[0] = 0x08;
2513+
if (compressed[1] == 0x19)
2514+
compressed[1] = 0x1d;
2515+
if (compressed[1] == 0x57)
2516+
compressed[1] = 0x5b;
2517+
if (compressed[1] == 0x95)
2518+
compressed[1] = 0x99;
2519+
if (compressed[1] == 0xd3)
2520+
compressed[1] = 0xd7;
2521+
}
24702522
std::string compressed_str((char*) compressed, comp_len);
24712523
free(compressed);
24722524
return compressed_str;
@@ -2541,19 +2593,30 @@ std::string generate_data(uint32 width, uint32 height, PNG_COLOR_SPACE_TYPE colo
25412593
}
25422594
}
25432595

2596+
int windowBits = 15;
25442597
if (!file_acc.generate) {
25452598
assert_cond(data_len == data.length(), "wrong length for uncompressed IDAT data");
25462599
file_acc.parse = NULL;
25472600
std::string compressed((char*)&file_acc.file_buffer[file_acc.file_pos], ::g->length());
2548-
for (int l = 0; l < 10; ++l)
2549-
if (compress_data(data, l) == compressed) {
2601+
int cinfo = (unsigned) compressed[0] >> 4;
2602+
if (0 <= cinfo && cinfo <= 7)
2603+
windowBits = cinfo + 8;
2604+
for (int l = 0; l < 10; ++l) {
2605+
std::string candidate = compress_data(data, l, windowBits);
2606+
if (candidate == compressed) {
25502607
file_acc.parse = [l](unsigned char* file_buf) -> long long { return l + 1; };
25512608
break;
25522609
}
2610+
}
25532611
assert_cond(!!file_acc.parse, "failed to find working compression level");
25542612
}
25552613
int level = file_acc.rand_int(11, file_acc.parse) - 1;
2556-
return compress_data(data, level);
2614+
file_acc.parse = NULL;
2615+
if (!file_acc.generate) {
2616+
file_acc.parse = [windowBits](unsigned char* file_buf) -> long long { return windowBits - 8; };
2617+
}
2618+
windowBits = file_acc.rand_int(8, file_acc.parse) + 8;
2619+
return compress_data(data, level, windowBits);
25572620
}
25582621

25592622

0 commit comments

Comments
 (0)