Skip to content
146 changes: 78 additions & 68 deletions src/lodepng/lodepng.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1629,11 +1629,9 @@ static unsigned getValueRequiredBits(unsigned char value) {

/*stats must already have been inited. */
unsigned lodepng_compute_color_stats(LodePNGColorStats* stats,
const unsigned char* in, unsigned w, unsigned h,
const unsigned char* in, const size_t numpixels,
const LodePNGColorMode* mode_in) {
size_t i;
ColorTree tree;
size_t numpixels = (size_t)w * (size_t)h;
unsigned error = 0;

/* mark things as done already if it would be impossible to have a more expensive case */
Expand Down Expand Up @@ -1706,6 +1704,7 @@ unsigned lodepng_compute_color_stats(LodePNGColorStats* stats,
}
}
} else /* < 16-bit */ {
ColorTree tree;
color_tree_init(&tree);
unsigned char r = 0, g = 0, b = 0, a = 0;
for(i = 0; i != numpixels; ++i) {
Expand Down Expand Up @@ -1766,6 +1765,8 @@ unsigned lodepng_compute_color_stats(LodePNGColorStats* stats,
if(alpha_done && numcolors_done && colored_done && bits_done) break;
}

color_tree_cleanup(&tree);

if(stats->key && !stats->alpha) {
for(i = 0; i != numpixels; ++i) {
getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
Expand All @@ -1783,13 +1784,7 @@ unsigned lodepng_compute_color_stats(LodePNGColorStats* stats,
stats->key_r += (stats->key_r << 8);
stats->key_g += (stats->key_g << 8);
stats->key_b += (stats->key_b << 8);
color_tree_cleanup(&tree);
}

unsigned char r = 0, g = 0, b = 0, a = 0;
getPixelColorRGBA8(&r, &g, &b, &a, in, 0, mode_in);
stats->white = stats->numcolors == 1 && stats->colored == 0 && r == 255 && w > 20 && h > 20 && ((w>225 && h > 225) || w*h > 75000 || (w> 250 && w*h > 40000));

return 0;
}

Expand Down Expand Up @@ -2093,36 +2088,32 @@ output image, e.g. grey if there are only grayscale pixels, palette if there
are less than 256 colors, ...
Updates values of mode with a potentially smaller color model. mode_out should
contain the user chosen color model, but will be overwritten with the new chosen one.*/
static unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out,
const unsigned char* image, unsigned w, unsigned h,
const LodePNGColorMode* mode_in, unsigned div) {
LodePNGColorStats prof;
static unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in,
const LodePNGColorStats* stats, size_t numpixels, unsigned div) {
unsigned error = 0;
unsigned palettebits;
unsigned palettebits, palette_ok, gray_ok;
size_t i, n;
lodepng_color_stats_init(&prof);
error = lodepng_compute_color_stats(&prof, image, w, h, mode_in);
if(error) return error;
unsigned palette_ok, gray_ok;

LodePNGColorStats* stats = &prof;
unsigned alpha = stats->alpha;
unsigned key = stats->key;
unsigned bits = stats->bits;

mode_out->key_defined = 0;

if(stats->key && (unsigned long long)w * h <= 49) {
prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/
prof.key = 0;
if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
if(key && numpixels <= 49) {
alpha = 1; /*too few pixels to justify tRNS chunk overhead*/
key = 0;
if(bits < 8) bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
}

gray_ok = !stats->colored;
if(!gray_ok && prof.bits < 8) prof.bits = 8;
if(!gray_ok && bits < 8) bits = 8;

n = stats->numcolors;
palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8));
palette_ok = n <= 256 && prof.bits <= 8;
if(8 + n * 4 > (unsigned long long)w * h / div) {palette_ok = 0;} /*don't add palette overhead if image has only a few pixels*/
if(gray_ok && !prof.alpha && prof.bits <= palettebits && !prof.white) {palette_ok = 0;} /*gray is less overhead*/
palette_ok = n <= 256 && bits <= 8;
if(8 + n * 4 > numpixels / div) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/
if(gray_ok && !alpha && bits <= palettebits && !stats->white) palette_ok = 0; /*gray is less overhead*/

if(palette_ok) {
const unsigned char* p = stats->palette;
Expand All @@ -2135,9 +2126,9 @@ static unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out,
mode_out->colortype = LCT_PALETTE;
mode_out->bitdepth = palettebits;
} else /*8-bit or 16-bit per channel*/ {
mode_out->bitdepth = prof.bits;
mode_out->colortype = prof.alpha ? (prof.colored ? LCT_RGBA : LCT_GREY_ALPHA)
: (prof.colored ? LCT_RGB : LCT_GREY);
mode_out->bitdepth = bits;
mode_out->colortype = alpha ? (stats->colored ? LCT_RGBA : LCT_GREY_ALPHA)
: (stats->colored ? LCT_RGB : LCT_GREY);

if(stats->key) {
unsigned mask = (1u << mode_out->bitdepth) - 1u; /*stats always uses 16-bit, mask converts it*/
Expand Down Expand Up @@ -3287,47 +3278,53 @@ static void filterScanline(unsigned char* out, const unsigned char* scanline, co
size_t i;
switch(filterType) {
case 0: /*None*/
for(i = 0; i != length; ++i) out[i] = scanline[i];
memcpy(out, scanline, length);
break;
case 1: /*Sub*/
for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth];
case 1: { /*Sub*/
size_t j = 0;
memcpy(out, scanline, bytewidth);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since bytewidth will be at most 8, memcpy shouldn't make a difference here

for(i = bytewidth; i != length; ++i, ++j) out[i] = scanline[i] - scanline[j];
break;
}
case 2: /*Up*/
if(prevline) {
for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i];
} else {
for(i = 0; i != length; ++i) out[i] = scanline[i];
memcpy(out, scanline, length);
}
break;
case 3: /*Average*/
case 3: { /*Average*/
size_t j = 0;
if(prevline) {
for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1);
for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1);
for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1u);
for(i = bytewidth; i < length; ++i, ++j) out[i] = scanline[i] - ((scanline[j] + prevline[i]) >> 1u);
} else {
for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1);
memcpy(out, scanline, bytewidth);
for(i = bytewidth; i < length; ++i, ++j) out[i] = scanline[i] - (scanline[j] >> 1u);
}
break;
case 4: /*Paeth*/
}
case 4: { /*Paeth*/
size_t j = 0;
if(prevline) {
/*paethPredictor(0, prevline[i], 0) is always prevline[i]*/
for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]);
for(i = bytewidth; i < length; ++i) {
out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth]));
for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - prevline[i];
for(i = bytewidth; i != length; ++i, ++j) {
out[i] = scanline[i] - paethPredictor(scanline[j], prevline[i], prevline[j]);
}
} else {
for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
memcpy(out, scanline, bytewidth);
/*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/
for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]);
for(i = bytewidth; i != length; ++i, ++j) out[i] = scanline[i] - scanline[j];
}
break;
}
default: return; /*invalid filter type given*/
}
}

static void filterScanline2(unsigned char* scanline, const unsigned char* prevline,
size_t length, unsigned char filterType, unsigned char forReal) {
size_t length, unsigned char filterType) {
if (!filterType) {
for(int i = 0; i < length; i+=4) {
if (!scanline[i + 3]) {
Expand Down Expand Up @@ -3389,8 +3386,8 @@ static void filterScanline2(unsigned char* scanline, const unsigned char* prevli
}
}
}
} else if(filterType == 4 && forReal) {
if(!prevline) {
} else if(filterType == 4) { /*forReal var is always zero, so the code is commented out for now*/
/*if(!prevline) {
if(!scanline[3]) {
*(unsigned*)scanline = 0;
}
Expand All @@ -3414,7 +3411,7 @@ static void filterScanline2(unsigned char* scanline, const unsigned char* prevli
scanline[i + 2] = paethPredictor(scanline[i - 2], prevline[i], prevline[i - 2]);
}
}
}
}*/
}
}

Expand Down Expand Up @@ -3548,7 +3545,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
memcpy(rem, &in2[y * linebytes], linebytes * clean);
for(type = 0; type != 5; ++type) {
if(clean) {
filterScanline2(&in2[y * linebytes], prevline, linebytes, type, 0);
filterScanline2(&in2[y * linebytes], prevline, linebytes, type);
filterScanline(attempt[type], &in2[y * linebytes], prevline, linebytes, bytewidth, type);
} else {
filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
Expand Down Expand Up @@ -3583,7 +3580,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
if(clean) {
filterScanline2(&in2[y * linebytes], prevline, linebytes, bestType, 0);
filterScanline2(&in2[y * linebytes], prevline, linebytes, bestType);
prevline = &in2[y * linebytes];
} else {
prevline = &in[y * linebytes];
Expand Down Expand Up @@ -3642,7 +3639,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
{
if(clean) {
memcpy(linebuf, &in[y * linebytes], linebytes);
filterScanline2(linebuf, prevline2, linebytes, type, 0);
filterScanline2(linebuf, prevline2, linebytes, type);
filterScanline(attempt[type], linebuf, prevline2, linebytes, bytewidth, type);
} else {
filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
Expand Down Expand Up @@ -3670,7 +3667,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,

if(clean) {
memcpy(linebuf, &in[y * linebytes], linebytes);
filterScanline2(linebuf, prevline2, linebytes, bestType, 0);
filterScanline2(linebuf, prevline2, linebytes, bestType);
filterScanline(attempt[bestType], linebuf, prevline2, linebytes, bytewidth, bestType);
} else {
filterScanline(attempt[bestType], &in[y * linebytes], prevline, linebytes, bytewidth, bestType);
Expand All @@ -3688,7 +3685,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
prevline = &in[y * linebytes];
if(clean) {
memcpy(linebuf, &in[y * linebytes], linebytes);
filterScanline2(linebuf, prevline2, linebytes, bestType, 0);
filterScanline2(linebuf, prevline2, linebytes, bestType);
memcpy(prevlinebuf, linebuf, linebytes);
prevline2 = prevlinebuf;
}
Expand Down Expand Up @@ -3723,7 +3720,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
for(type = 0; type != 5; ++type) {
size_t sum = 0;
if(clean) {
filterScanline2(&in2[y * linebytes], prevline, linebytes, type, 0);
filterScanline2(&in2[y * linebytes], prevline, linebytes, type);
filterScanline(attempt[type], &in2[y * linebytes], prevline, linebytes, bytewidth, type);
} else {
filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
Expand Down Expand Up @@ -3756,7 +3753,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
if(clean) {
filterScanline2(&in2[y * linebytes], prevline, linebytes, bestType, 0);
filterScanline2(&in2[y * linebytes], prevline, linebytes, bestType);
prevline = &in2[y * linebytes];
} else {
prevline = &in[y * linebytes];
Expand All @@ -3783,7 +3780,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
for(type = 0; type != 5; ++type) {
size_t sum = 0;
if(clean) {
filterScanline2(&in2[y * linebytes], prevline, linebytes, type, 0);
filterScanline2(&in2[y * linebytes], prevline, linebytes, type);
filterScanline(attempt[type], &in2[y * linebytes], prevline, linebytes, bytewidth, type);
} else {
filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
Expand All @@ -3808,7 +3805,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
if(clean) {
filterScanline2(&in2[y * linebytes], prevline, linebytes, bestType, 0);
filterScanline2(&in2[y * linebytes], prevline, linebytes, bestType);
prevline = &in2[y * linebytes];
} else {
prevline = &in[y * linebytes];
Expand All @@ -3834,7 +3831,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
for(type = 0; type != 5; ++type) {
size_t sum = 0;
if(clean) {
filterScanline2(&in2[y * linebytes], prevline, linebytes, type, 0);
filterScanline2(&in2[y * linebytes], prevline, linebytes, type);
filterScanline(attempt[type], &in2[y * linebytes], prevline, linebytes, bytewidth, type);
} else {
filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
Expand All @@ -3859,7 +3856,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
if(clean) {
filterScanline2(&in2[y * linebytes], prevline, linebytes, bestType, 0);
filterScanline2(&in2[y * linebytes], prevline, linebytes, bestType);
prevline = &in2[y * linebytes];
} else {
prevline = &in[y * linebytes];
Expand All @@ -3883,7 +3880,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
/*try the 5 filter types*/
for(type = 0; type != 5; ++type) {
if(clean) {
filterScanline2(&in2[y * linebytes], prevline, linebytes, type, 0);
filterScanline2(&in2[y * linebytes], prevline, linebytes, type);
filterScanline(attempt[type], &in2[y * linebytes], prevline, linebytes, bytewidth, type);
} else {
filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
Expand All @@ -3910,7 +3907,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
if(clean) {
filterScanline2(&in2[y * linebytes], prevline, linebytes, bestType, 0);
filterScanline2(&in2[y * linebytes], prevline, linebytes, bestType);
prevline = &in2[y * linebytes];
} else {
prevline = &in[y * linebytes];
Expand Down Expand Up @@ -3981,7 +3978,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
out[y * (linebytes + 1)] = type;
if(clean) {
memcpy(linebuf, &in[y * linebytes], linebytes);
filterScanline2(linebuf, prevline, linebytes, type, 0);
filterScanline2(linebuf, prevline, linebytes, type);
filterScanline(&out[y * (linebytes + 1) + 1], linebuf, prevline, linebytes, bytewidth, type);
memcpy(prevlinebuf, linebuf, linebytes);
prevline = prevlinebuf;
Expand Down Expand Up @@ -4074,7 +4071,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
out[y * (linebytes + 1)] = type;
if(clean) {
memcpy(linebuf, &in[y * linebytes], linebytes);
filterScanline2(linebuf, prevline, linebytes, type, 0);
filterScanline2(linebuf, prevline, linebytes, type);
filterScanline(&out[y * (linebytes + 1) + 1], linebuf, prevline, linebytes, bytewidth, type);
memcpy(prevlinebuf, linebuf, linebytes);
prevline = prevlinebuf;
Expand Down Expand Up @@ -4104,7 +4101,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w,
out[y * (linebytes + 1)] = type;
if(clean) {
memcpy(linebuf, &in[y * linebytes], linebytes);
filterScanline2(linebuf, prevline, linebytes, type, 0);
filterScanline2(linebuf, prevline, linebytes, type);
filterScanline(&out[y * (linebytes + 1) + 1], linebuf, prevline, linebytes, bytewidth, type);
memcpy(prevlinebuf, linebuf, linebytes);
prevline = prevlinebuf;
Expand Down Expand Up @@ -4292,6 +4289,7 @@ static unsigned lodepng_encode(unsigned char** out, size_t* outsize,
LodePNGState* state, LodePNGPaletteSettings palset) {
unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/
size_t datasize = 0;
size_t numpixels = (size_t)w * (size_t)h;
ucvector outv = ucvector_init(0, 0);
LodePNGInfo info;
const LodePNGInfo* info_png = &state->info_png;
Expand Down Expand Up @@ -4324,7 +4322,19 @@ static unsigned lodepng_encode(unsigned char** out, size_t* outsize,
/* color convert and compute scanline filter types */
lodepng_info_copy(&info, &state->info_png);
if(state->encoder.auto_convert) {
state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw, state->div);
LodePNGColorStats stats;
lodepng_color_stats_init(&stats);

state->error = lodepng_compute_color_stats(&stats, image, numpixels, &state->info_raw);
if(state->error) goto cleanup;
else { /*check if image is white only if no error is detected in previous function*/
unsigned char r = 0, g = 0, b = 0, a = 0;
getPixelColorRGBA8(&r, &g, &b, &a, image, 0, &state->info_raw);
stats.white = stats.numcolors == 1 && stats.colored == 0 && r == 255 && w > 20 && h > 20
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this get moved?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reduces the number of variables passed to lodepng_compute_color_stats by one, this change also gets around calculating numpixels twice

&& ((w > 225 && h > 225) || numpixels > 75000 || (w > 250 && numpixels > 40000));
}

state->error = lodepng_auto_choose_color(&info.color, &state->info_raw, &stats, numpixels, state->div);
if(state->error) goto cleanup;
if(info.color.colortype == LCT_PALETTE && palset.order != LPOS_NONE) {
if (palset._first & 1) {
Expand All @@ -4351,7 +4361,7 @@ static unsigned lodepng_encode(unsigned char** out, size_t* outsize,
}
if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) {
unsigned char* converted;
size_t size = ((size_t)w * (size_t)h * (size_t)lodepng_get_bpp(&info.color) + 7u) / 8u;
size_t size = (numpixels * (size_t)lodepng_get_bpp(&info.color) + 7u) / 8u;

converted = (unsigned char*)lodepng_malloc(size);
if(!converted && size) state->error = 83; /*alloc fail*/
Expand Down