summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLode <lvandeve@gmail.com>2016-11-28 01:10:26 +0100
committerLode <lvandeve@gmail.com>2016-11-28 01:10:26 +0100
commit1d1b55c071591d9537bf5adf67af8d473af6beb8 (patch)
tree46cff2806790e79d425972c549e833e4fd99b6e4
parentac8f8863c609f34b69d629ed72d6a234afeb11d5 (diff)
grey+alpha auto color model detection bugfix
-rw-r--r--lodepng.cpp24
-rw-r--r--lodepng.h9
-rw-r--r--lodepng_unittest.cpp79
3 files changed, 86 insertions, 26 deletions
diff --git a/lodepng.cpp b/lodepng.cpp
index 8c78758..bf237df 100644
--- a/lodepng.cpp
+++ b/lodepng.cpp
@@ -1,5 +1,5 @@
/*
-LodePNG version 20160501
+LodePNG version 20161127
Copyright (c) 2005-2016 Lode Vandevenne
@@ -39,7 +39,7 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for
#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
#endif /*_MSC_VER */
-const char* LODEPNG_VERSION_STRING = "20160501";
+const char* LODEPNG_VERSION_STRING = "20161127";
/*
This source file is built up in the following large parts. The code sections
@@ -3534,8 +3534,8 @@ void lodepng_color_profile_init(LodePNGColorProfile* profile)
{
profile->colored = 0;
profile->key = 0;
- profile->alpha = 0;
profile->key_r = profile->key_g = profile->key_b = 0;
+ profile->alpha = 0;
profile->numcolors = 0;
profile->bits = 1;
}
@@ -3622,8 +3622,8 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile,
if(a != 65535 && (a != 0 || (profile->key && !matchkey)))
{
profile->alpha = 1;
+ profile->key = 0;
alpha_done = 1;
- if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
}
else if(a == 0 && !profile->alpha && !profile->key)
{
@@ -3636,6 +3636,7 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile,
{
/* Color key cannot be used if an opaque pixel also has that RGB color. */
profile->alpha = 1;
+ profile->key = 0;
alpha_done = 1;
}
}
@@ -3651,6 +3652,7 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile,
{
/* Color key cannot be used if an opaque pixel also has that RGB color. */
profile->alpha = 1;
+ profile->key = 0;
alpha_done = 1;
}
}
@@ -3684,6 +3686,7 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile,
if(a != 255 && (a != 0 || (profile->key && !matchkey)))
{
profile->alpha = 1;
+ profile->key = 0;
alpha_done = 1;
if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
}
@@ -3698,6 +3701,7 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile,
{
/* Color key cannot be used if an opaque pixel also has that RGB color. */
profile->alpha = 1;
+ profile->key = 0;
alpha_done = 1;
if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
}
@@ -3734,7 +3738,9 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile,
{
/* Color key cannot be used if an opaque pixel also has that RGB color. */
profile->alpha = 1;
+ profile->key = 0;
alpha_done = 1;
+ if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
}
}
}
@@ -3760,7 +3766,7 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out,
{
LodePNGColorProfile prof;
unsigned error = 0;
- unsigned i, n, palettebits, grey_ok, palette_ok;
+ unsigned i, n, palettebits, palette_ok;
lodepng_color_profile_init(&prof);
error = lodepng_get_color_profile(&prof, image, w, h, mode_in);
@@ -3770,14 +3776,14 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out,
if(prof.key && w * h <= 16)
{
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*/
}
- grey_ok = !prof.colored && !prof.alpha; /*grey without alpha, with potentially low bits*/
n = prof.numcolors;
palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8));
- palette_ok = n <= 256 && (n * 2 < w * h) && prof.bits <= 8;
+ palette_ok = n <= 256 && prof.bits <= 8;
if(w * h < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/
- if(grey_ok && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/
+ if(!prof.colored && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/
if(palette_ok)
{
@@ -3806,7 +3812,7 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out,
mode_out->colortype = prof.alpha ? (prof.colored ? LCT_RGBA : LCT_GREY_ALPHA)
: (prof.colored ? LCT_RGB : LCT_GREY);
- if(prof.key && !prof.alpha)
+ if(prof.key)
{
unsigned mask = (1u << mode_out->bitdepth) - 1u; /*profile always uses 16-bit, mask converts it*/
mode_out->key_r = prof.key_r & mask;
diff --git a/lodepng.h b/lodepng.h
index fcf9f71..8c634d2 100644
--- a/lodepng.h
+++ b/lodepng.h
@@ -1,5 +1,5 @@
/*
-LodePNG version 20160501
+LodePNG version 20161127
Copyright (c) 2005-2016 Lode Vandevenne
@@ -559,11 +559,11 @@ Used internally by default if "auto_convert" is enabled. Public because it's use
typedef struct LodePNGColorProfile
{
unsigned colored; /*not greyscale*/
- unsigned key; /*if true, image is not opaque. Only if true and alpha is false, color key is possible.*/
- unsigned short key_r; /*these values are always in 16-bit bitdepth in the profile*/
+ unsigned key; /*image is not opaque and color key is possible instead of full alpha*/
+ unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/
unsigned short key_g;
unsigned short key_b;
- unsigned alpha; /*alpha channel or alpha palette required*/
+ unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/
unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/
unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/
unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.*/
@@ -1608,6 +1608,7 @@ yyyymmdd.
Some changes aren't backwards compatible. Those are indicated with a (!)
symbol.
+*) 27 nov 2016: grey+alpha auto color model detection bugfix
*) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort).
*) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within
the limits of pure C90).
diff --git a/lodepng_unittest.cpp b/lodepng_unittest.cpp
index 3d7f8e0..5297a35 100644
--- a/lodepng_unittest.cpp
+++ b/lodepng_unittest.cpp
@@ -93,6 +93,7 @@ g++ -I ./ lodepng.cpp examples/example_sdl.cpp -Wall -Wextra -pedantic -ansi -O3
*) strip trailing spaces and ensure consistent newlines
*) check diff of lodepng.cpp and lodepng.h before submitting
+git difftool -y
*/
@@ -613,7 +614,7 @@ void testColor(int r, int g, int b, int a)
// Tests combinations of various colors in different orders
void testFewColors()
{
- std::cout << "codec test colors " << std::endl;
+ std::cout << "codec test few colors " << std::endl;
Image image;
image.width = 20;
image.height = 20;
@@ -621,22 +622,20 @@ void testFewColors()
image.bitDepth = 8;
image.data.resize(image.width * image.height * 4);
std::vector<unsigned char> colors;
- colors.push_back(0); colors.push_back(0); colors.push_back(0); colors.push_back(255);
- colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(255);
- colors.push_back(128); colors.push_back(128); colors.push_back(128); colors.push_back(255);
- colors.push_back(0); colors.push_back(0); colors.push_back(255); colors.push_back(255);
- colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(0);
- colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(1);
+ colors.push_back(0); colors.push_back(0); colors.push_back(0); colors.push_back(255); // black
+ colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(255); // white
+ colors.push_back(128); colors.push_back(128); colors.push_back(128); colors.push_back(255); // grey
+ colors.push_back(0); colors.push_back(0); colors.push_back(255); colors.push_back(255); // blue
+ colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(0); // transparent white
+ colors.push_back(255); colors.push_back(255); colors.push_back(255); colors.push_back(1); // translucent white
for(size_t i = 0; i < colors.size(); i += 4)
for(size_t j = 0; j < colors.size(); j += 4)
for(size_t k = 0; k < colors.size(); k += 4)
for(size_t l = 0; l < colors.size(); l += 4)
{
+ //std::cout << (i/4) << " " << (j/4) << " " << (k/4) << " " << (l/4) << std::endl;
for(size_t c = 0; c < 4; c++)
{
- /*image.data[0 + c] = colors[i + c];
- image.data[4 + c] = colors[j + c];
- image.data[8 + c] = colors[k + c];*/
for(unsigned y = 0; y < image.height; y++)
for(unsigned x = 0; x < image.width; x++)
{
@@ -1778,35 +1777,50 @@ void testAutoColorModel(const std::vector<unsigned char>& colors, unsigned inbit
ASSERT_EQUALS(colortype, state.info_png.color.colortype);
ASSERT_EQUALS(bitdepth, state.info_png.color.bitdepth);
ASSERT_EQUALS(key, state.info_png.color.key_defined);
+ // also check that the PNG decoded correctly and has same colors as input
if(inbitdepth == 8) { for(size_t i = 0; i < colors.size(); i++) ASSERT_EQUALS(colors[i], decoded[i]); }
else { for(size_t i = 0; i < colors.size() / 2; i++) ASSERT_EQUALS(colors[i * 2], decoded[i]); }
}
void testAutoColorModels()
{
+ // 1-bit grey
std::vector<unsigned char> grey1;
for(size_t i = 0; i < 2; i++) addColor(grey1, i * 255, i * 255, i * 255, 255);
testAutoColorModel(grey1, 8, LCT_GREY, 1, false);
+ // 2-bit grey
std::vector<unsigned char> grey2;
for(size_t i = 0; i < 4; i++) addColor(grey2, i * 85, i * 85, i * 85, 255);
testAutoColorModel(grey2, 8, LCT_GREY, 2, false);
-
+ // 4-bit grey
std::vector<unsigned char> grey4;
for(size_t i = 0; i < 16; i++) addColor(grey4, i * 17, i * 17, i * 17, 255);
testAutoColorModel(grey4, 8, LCT_GREY, 4, false);
-
+ // 8-bit grey
std::vector<unsigned char> grey8;
for(size_t i = 0; i < 256; i++) addColor(grey8, i, i, i, 255);
testAutoColorModel(grey8, 8, LCT_GREY, 8, false);
-
+ // 16-bit grey
std::vector<unsigned char> grey16;
for(size_t i = 0; i < 257; i++) addColor16(grey16, i, i, i, 65535);
testAutoColorModel(grey16, 16, LCT_GREY, 16, false);
+ // 8-bit grey+alpha
+ std::vector<unsigned char> grey8a;
+ for(size_t i = 0; i < 17; i++) addColor(grey8a, i, i, i, i);
+ testAutoColorModel(grey8a, 8, LCT_GREY_ALPHA, 8, false);
+
+ // 16-bit grey+alpha
+ std::vector<unsigned char> grey16a;
+ for(size_t i = 0; i < 257; i++) addColor16(grey16a, i, i, i, i);
+ testAutoColorModel(grey16a, 16, LCT_GREY_ALPHA, 16, false);
+
+
+ // various palette tests
std::vector<unsigned char> palette;
addColor(palette, 0, 0, 1, 255);
testAutoColorModel(palette, 8, LCT_PALETTE, 1, false);
@@ -1823,43 +1837,73 @@ void testAutoColorModels()
addColor(palette, 0, 0, 18, 1); // translucent
testAutoColorModel(palette, 8, LCT_PALETTE, 8, false);
+ // 1-bit grey + alpha not possible, becomes palette
+ std::vector<unsigned char> grey1a;
+ for(size_t i = 0; i < 2; i++) addColor(grey1a, i, i, i, 128);
+ testAutoColorModel(grey1a, 8, LCT_PALETTE, 1, false);
+
+ // 2-bit grey + alpha not possible, becomes palette
+ std::vector<unsigned char> grey2a;
+ for(size_t i = 0; i < 4; i++) addColor(grey2a, i, i, i, 128);
+ testAutoColorModel(grey2a, 8, LCT_PALETTE, 2, false);
+
+ // 4-bit grey + alpha not possible, becomes palette
+ std::vector<unsigned char> grey4a;
+ for(size_t i = 0; i < 16; i++) addColor(grey4a, i, i, i, 128);
+ testAutoColorModel(grey4a, 8, LCT_PALETTE, 4, false);
+
+ // 8-bit rgb
std::vector<unsigned char> rgb = grey8;
addColor(rgb, 255, 0, 0, 255);
testAutoColorModel(rgb, 8, LCT_RGB, 8, false);
+ // 8-bit rgb + key
std::vector<unsigned char> rgb_key = rgb;
addColor(rgb_key, 128, 0, 0, 0);
testAutoColorModel(rgb_key, 8, LCT_RGB, 8, true);
+ // 8-bit rgb, not key due to edge case: single key color, but opaque color has same RGB value
std::vector<unsigned char> rgb_key2 = rgb_key;
addColor(rgb_key2, 128, 0, 0, 255); // same color but opaque ==> no more key
testAutoColorModel(rgb_key2, 8, LCT_RGBA, 8, false);
+ // 8-bit rgb, not key due to semi translucent
std::vector<unsigned char> rgb_key3 = rgb_key;
addColor(rgb_key3, 128, 0, 0, 255); // semi-translucent ==> no more key
testAutoColorModel(rgb_key3, 8, LCT_RGBA, 8, false);
+ // 8-bit rgb, not key due to multiple transparent colors
std::vector<unsigned char> rgb_key4 = rgb_key;
addColor(rgb_key4, 128, 0, 0, 255);
addColor(rgb_key4, 129, 0, 0, 255); // two different transparent colors ==> no more key
testAutoColorModel(rgb_key4, 8, LCT_RGBA, 8, false);
+ // 1-bit grey with key
std::vector<unsigned char> grey1_key = grey1;
grey1_key[7] = 0;
testAutoColorModel(grey1_key, 8, LCT_GREY, 1, true);
+ // 2-bit grey with key
std::vector<unsigned char> grey2_key = grey2;
grey2_key[7] = 0;
testAutoColorModel(grey2_key, 8, LCT_GREY, 2, true);
+ // 4-bit grey with key
std::vector<unsigned char> grey4_key = grey4;
grey4_key[7] = 0;
testAutoColorModel(grey4_key, 8, LCT_GREY, 4, true);
+ // 8-bit grey with key
std::vector<unsigned char> grey8_key = grey8;
grey8_key[7] = 0;
testAutoColorModel(grey8_key, 8, LCT_GREY, 8, true);
+ // 16-bit grey with key
+ std::vector<unsigned char> grey16_key = grey16;
+ grey16_key[14] = grey16_key[15] = 0;
+ testAutoColorModel(grey16_key, 16, LCT_GREY, 16, true);
+
+ // a single 16-bit color, can't become palette due to being 16-bit
std::vector<unsigned char> small16;
addColor16(small16, 1, 0, 0, 65535);
testAutoColorModel(small16, 16, LCT_RGB, 16, false);
@@ -1868,13 +1912,22 @@ void testAutoColorModels()
addColor16(small16a, 1, 0, 0, 1);
testAutoColorModel(small16a, 16, LCT_RGBA, 16, false);
+ // what we provide as 16-bit is actually representable as 8-bit, so 8-bit palette expected for single color
std::vector<unsigned char> not16;
addColor16(not16, 257, 257, 257, 0);
testAutoColorModel(not16, 16, LCT_PALETTE, 1, false);
+ // the rgb color is representable as 8-bit, but the alpha channel only as 16-bit, so ensure it uses 16-bit and not palette for this single color
std::vector<unsigned char> alpha16;
addColor16(alpha16, 257, 0, 0, 10000);
testAutoColorModel(alpha16, 16, LCT_RGBA, 16, false);
+
+ // 1-bit grey, with attempt to get color key but can't do it due to opaque color with same value
+ std::vector<unsigned char> grey1k;
+ addColor(grey1k, 0, 0, 0, 255);
+ addColor(grey1k, 255, 255, 255, 255);
+ addColor(grey1k, 255, 255, 255, 0);
+ testAutoColorModel(grey1k, 8, LCT_PALETTE, 2, false);
}
void testPaletteToPaletteDecode() {