[libpng16] Finish patching pngstest.c
diff --git a/contrib/libtests/pngstest.c b/contrib/libtests/pngstest.c
index c0b2cd5..c1cdf57 100644
--- a/contrib/libtests/pngstest.c
+++ b/contrib/libtests/pngstest.c
@@ -98,6 +98,100 @@
return YfromRGB(r, g, b);
}
+/* The error that results from using a 2.2 power law in place of the correct
+ * sRGB transform, given an 8-bit value which might be either sRGB or power-law.
+ */
+static int
+power_law_error8(int value)
+{
+ if (value > 0 && value < 255)
+ {
+ double vd = value / 255.;
+ double e = fabs(
+ pow(linear_from_sRGB(vd), 1/2.2) - sRGB_from_linear(pow(vd, 2.2)));
+
+ /* TODO: remove this, it's a math check */
+ if (e*255 >= 17) abort();
+
+ /* Always allow an extra 1 here for rounding errors */
+ e = 1+floor(255 * e);
+ return (int)e;
+ }
+
+ return 0;
+}
+
+static int error_in_sRGB_roundtrip = 56; /* by experiment */
+static int
+power_law_error16(int value)
+{
+ if (value > 0 && value < 65535)
+ {
+ /* Round trip the value through an 8-bit representation but using
+ * non-matching to/from convertions.
+ */
+ double vd = value / 65535.;
+ double e = fabs(
+ pow(sRGB_from_linear(vd), 2.2) - linear_from_sRGB(pow(vd, 1/2.2)));
+
+ /* Always allow an extra 1 here for rounding errors */
+ e = error_in_sRGB_roundtrip+floor(65535 * e);
+ return (int)e;
+ }
+
+ return 0;
+}
+
+static int
+compare_8bit(int v1, int v2, int error_limit, int multiple_algorithms)
+{
+ int e = abs(v1-v2);
+ int ev1, ev2;
+
+ if (e <= error_limit)
+ return 1;
+
+ if (!multiple_algorithms)
+ return 0;
+
+ ev1 = power_law_error8(v1);
+ if (e <= ev1)
+ return 1;
+
+ ev2 = power_law_error8(v2);
+ if (e <= ev2)
+ return 1;
+
+ return 0;
+}
+
+static int
+compare_16bit(int v1, int v2, int error_limit, int multiple_algorithms)
+{
+ int e = abs(v1-v2);
+ int ev1, ev2;
+
+ if (e <= error_limit)
+ return 1;
+
+ /* "multiple_algorithms" in this case means that a color-map has been
+ * involved somewhere, so we can deduce that the values were forced to 8-bit
+ * (like the via_linear case for 8-bit.)
+ */
+ if (!multiple_algorithms)
+ return 0;
+
+ ev1 = power_law_error16(v1);
+ if (e <= ev1)
+ return 1;
+
+ ev2 = power_law_error16(v2);
+ if (e <= ev2)
+ return 1;
+
+ return 0;
+}
+
#define READ_FILE 1 /* else memory */
#define USE_STDIO 2 /* else use file name */
#define USE_BACKGROUND 4 /* else composite in place */
@@ -128,6 +222,7 @@
* pngstest.
*/
#define FORMAT_COUNT 64
+#define FORMAT_MASK 0x3f
static PNG_CONST char * PNG_CONST format_names[FORMAT_COUNT] =
{
"sRGB-gray",
@@ -289,7 +384,6 @@
png_uint_32 opts;
const char *file_name;
int stride_extra;
- int cmap_size;
FILE *input_file;
png_voidp input_memory;
png_size_t input_memory_size;
@@ -388,6 +482,7 @@
image->buffer = voidcast(png_bytep, malloc(size+32));
if (image->buffer == NULL)
{
+ fflush(stdout);
fprintf(stderr,
"simpletest: out of memory allocating %lu(+32) byte buffer\n",
(unsigned long)size);
@@ -420,12 +515,14 @@
{
if (check16(image->buffer, 95))
{
+ fflush(stdout);
fprintf(stderr, "%s: overwrite at start of image buffer\n", arg);
exit(1);
}
if (check16(image->buffer+16+image->allocsize, 95))
{
+ fflush(stdout);
fprintf(stderr, "%s: overwrite at end of image buffer\n", arg);
exit(1);
}
@@ -437,6 +534,7 @@
static int
logerror(Image *image, const char *a1, const char *a2, const char *a3)
{
+ fflush(stdout);
if (image->image.warning_or_error)
fprintf(stderr, "%s%s%s: %s\n", a1, a2, a3, image->image.message);
@@ -513,14 +611,9 @@
int result = 1;
if (format & PNG_FORMAT_FLAG_COLORMAP)
- {
- /* The actual sample is in the color-map, indexed by the pixel */
- pixel->format = PNG_FORMAT_OF_COLORMAP(format);
pp = image->colormap + PNG_IMAGE_SAMPLE_SIZE(format) * *pp;
- }
- else
- pixel->format = format;
+ pixel->format = format;
/* Initialize the alpha values for opaque: */
pixel->a8 = 255;
@@ -529,6 +622,7 @@
switch (PNG_IMAGE_SAMPLE_COMPONENT_SIZE(format))
{
default:
+ fflush(stdout);
fprintf(stderr, "pngstest: impossible sample component size: %lu\n",
(unsigned long)PNG_IMAGE_SAMPLE_COMPONENT_SIZE(format));
exit(1);
@@ -574,6 +668,9 @@
/* 'a1' is 1/65535 * 1/alpha, for alpha in the range 0..1 */
if (pixel->a16 == 0)
{
+ if (pixel->r16 > 0 || pixel->g16 > 0 || pixel->b16 > 0)
+ result = 0;
+
pixel->r8 = pixel->g8 = pixel->b8 = pixel->y8 = 255;
pixel->a8 = 0;
}
@@ -690,7 +787,8 @@
static int error_in_premultiply = 1;
static const char *
-cmppixel(Pixel *a, Pixel *b, const png_color *background, int via_linear)
+cmppixel(Pixel *a, Pixel *b, const png_color *background, int via_linear,
+ int multiple_algorithms)
{
int error_limit = 0;
@@ -699,14 +797,46 @@
/* If the input was non-opaque then use the pre-multiplication error
* limit.
*/
- if ((a->format & PNG_FORMAT_FLAG_ALPHA) && a->a16 < 65535)
+ if ((a->format & PNG_FORMAT_FLAG_ALPHA) && a->a16 < 65535 &&
+ error_limit < error_in_premultiply)
error_limit = error_in_premultiply;
if (b->format & PNG_FORMAT_FLAG_ALPHA)
{
- /* Expect an exact match. */
- if (b->a16 != a->a16)
- return "linear alpha mismatch";
+ if ((b->format & PNG_FORMAT_FLAG_COLORMAP) == 0 ||
+ (a->format & PNG_FORMAT_FLAG_COLORMAP) != 0 ||
+ (a->format & PNG_FORMAT_FLAG_ALPHA) == 0)
+ {
+ /* Expect an exact match. */
+ if (b->a16 != a->a16)
+ return "linear alpha mismatch";
+ }
+
+ else
+ {
+ /* Transform from non-color-mapped format with alpha to color-map
+ * with alpha. Most alphs is lost.
+ */
+ if (b->format & PNG_FORMAT_FLAG_COLOR)
+ {
+ /* Color; three levels of alpha (only!) */
+ if (abs(b->a16 - a->a16) > 16384)
+ return "linear color-mapped color alpha mismatch";
+ }
+
+ else
+ {
+ /* Grayscale (GA palette), 6 levels of alpha. */
+ if (abs(b->a16 - a->a16) > 6554)
+ return "linear color-mapped gray alpha mismatch";
+ }
+
+ /* If the alpha ends up as zero skip any check on the color
+ * components.
+ */
+ if (b->a16 == 0 && b->y16 == 0)
+ return NULL;
+ }
}
else if (a->format & PNG_FORMAT_FLAG_ALPHA)
@@ -735,9 +865,86 @@
}
}
- if (abs(a->r16-b->r16) <= error_limit &&
- abs(a->g16-b->g16) <= error_limit &&
- abs(a->b16-b->b16) <= error_limit)
+ /* Well, ok, if the file is color-mapped the color-mapping probably
+ * used colors spaced at 51 in sRGB space, so there is massive drift to
+ * be allowed here.
+ */
+ if (b->format & PNG_FORMAT_FLAG_COLORMAP)
+ {
+ /* If the input (a) was detectably grayscale then just permit the
+ * grayscale errors; we require libpng to at least do this.
+ */
+ if ((a->format & PNG_FORMAT_FLAG_COLOR) == 0)
+ {
+ png_byte v = isRGB(a->y16);
+
+ if (b->r8 == v && b->g8 == v && b->b8 == v)
+ return NULL;
+
+ if ((a->format & PNG_FORMAT_FLAG_ALPHA) != 0 &&
+ (b->format & PNG_FORMAT_FLAG_ALPHA) == 0) /* alpha removed */
+ {
+ /* Alpha was removed by compose-on-black; fix up the pixel a
+ * '8-bit' values to match.
+ */
+ a->r8 = isRGB(a->r16);
+ a->g8 = isRGB(a->g16);
+ a->b8 = isRGB(a->b16);
+ a->y8 = isRGB(a->y16);
+
+ if (b->y8 == 255 && a->y8 == 254)
+ return NULL; /* transparency hacked 254->255 */
+
+ else if (b->y8 == 254 && a->a8 != 0)
+ return "possible error in transparency hack (color)";
+ }
+
+ if ((b->format & PNG_FORMAT_FLAG_ALPHA) != 0)
+ {
+ /* GA color-map; limited accuracy for opaque pixels, +/- 26
+ * accuracy for partially transparent ones.
+ */
+ if (error_limit < 1)
+ error_limit = 1;
+
+ if (a->a8 > 0 && a->a8 < 255)
+ {
+ if (error_limit < 26)
+ error_limit = 26;
+ }
+ }
+ }
+
+ else /* input is not detectably grayscale */
+ {
+ /* The input was forced into an sRGB 666 color-map; error +/-26,
+ * guess the error limit from the actual input values.
+ */
+ int red = (isRGB(a->r16)+25)/51;
+ int green = (isRGB(a->g16)+25)/51;
+ int blue = (isRGB(a->b16)+25)/51;
+
+ if ((red-1)*51 <= b->r8 && (red+1)*51 >= b->r8 &&
+ (green-1)*51 <= b->g8 && (green+1)*51 >= b->g8 &&
+ (blue-1)*51 <= b->b8 && (blue+1)*51 >= b->b8)
+ return NULL;
+
+ return "666 color-map error";
+ }
+
+ /* Now compare the 8-bit values, not the 16-bit ones. */
+ if (compare_8bit(a->r8, b->r8, error_limit, multiple_algorithms) &&
+ compare_8bit(a->g8, b->g8, error_limit, multiple_algorithms) &&
+ compare_8bit(a->b8, b->b8, error_limit, multiple_algorithms))
+ return NULL;
+
+ return "linear color-map color mismatch";
+ }
+
+ else if (compare_16bit(a->r16, b->r16, error_limit,
+ multiple_algorithms) &&
+ compare_16bit(a->g16, b->g16, error_limit, multiple_algorithms) &&
+ compare_16bit(a->b16, b->b16, error_limit, multiple_algorithms))
return NULL;
return err;
@@ -748,7 +955,7 @@
const char *err = "linear gray mismatch";
/* Check for an exact match. */
- if (a->y16 == b->y16)
+ if (a->y16 == b->y16 && a->a16 == b->a16)
return NULL;
/* Not an exact match; allow drift only if the input is 8-bit or if it
@@ -778,7 +985,59 @@
}
}
- if (abs(a->y16-b->y16) <= error_limit)
+ if (b->format & PNG_FORMAT_FLAG_COLORMAP)
+ {
+ /* Forced into a colormap, since the format is a grayscale one we
+ * can calculate the permitted error from the sRGB bucket the value
+ * should fall into.
+ */
+ png_byte v = a->y8;
+
+ if (b->y8 == v && a->a8 == b->a8)
+ return NULL;
+
+ if ((a->format & PNG_FORMAT_FLAG_ALPHA) != 0 &&
+ (b->format & PNG_FORMAT_FLAG_ALPHA) == 0) /* alpha removed */
+ {
+ /* Alpha was removed by compose-on-black; fix up the pixel a
+ * '8-bit' values to match.
+ */
+ a->r8 = isRGB(a->r16);
+ a->g8 = isRGB(a->g16);
+ a->b8 = isRGB(a->b16);
+ a->y8 = isRGB(a->y16);
+
+ if (b->y8 == 255 && a->y8 == 254)
+ return NULL; /* transparency hacked 254->255 */
+
+ else if (b->y8 == 254 && a->a8 != 0)
+ return "possible error in transparency hack (gray)";
+ }
+
+ if ((b->format & PNG_FORMAT_FLAG_ALPHA) != 0)
+ {
+ /* GA color-map; limited accuracy for opaque pixels, +/- 26
+ * accuracy for partially transparent ones.
+ */
+ if (error_limit < 1)
+ error_limit = 1;
+
+ if (a->a8 > 0 && a->a8 < 255)
+ {
+ if (error_limit < 26)
+ error_limit = 26;
+ }
+ }
+
+ /* And compare the 8-bit values, not the 16-bit ones. */
+ if (compare_8bit(a->y8, b->y8, error_limit, multiple_algorithms))
+ return NULL;
+
+ return "linear color-map gray mismatch";
+ }
+
+ else if (compare_16bit(a->y16, b->y16, error_limit,
+ multiple_algorithms))
return NULL;
return err;
@@ -858,6 +1117,29 @@
if (b->a8 != a->a8)
return "8-bit alpha mismatch";
+ /* If the input was not color-mapped but the output is transparent
+ * pixels will have been forced to just one palette entry, with the
+ * value 255,255,255,0.
+ */
+ if ((a->format & PNG_FORMAT_FLAG_COLORMAP) == 0 &&
+ (b->format & PNG_FORMAT_FLAG_COLORMAP) != 0 &&
+ (a->format & PNG_FORMAT_FLAG_ALPHA) != 0 &&
+ a->a16 == 0)
+ {
+ if (b->format & PNG_FORMAT_FLAG_COLOR)
+ {
+ if (b->r8 == 255 && b->g8 == 255 && b->b8 == 255 && b->a8 == 0)
+ return NULL;
+
+ return "bad RGB color-map transparent entry";
+ }
+
+ else if (b->y8 == 255 && b->a8 == 0)
+ return NULL;
+
+ return "bad gray color-map transparent entry";
+ }
+
/* If the *input* was linear+alpha as well libpng will have converted
* the non-premultiplied format directly to the sRGB non-premultiplied
* format and the precision loss on an intermediate pre-multiplied
@@ -1000,6 +1282,11 @@
a->r8 = a->g8 = a->b8 = a->y8 = sRGB(y/65535);
}
}
+
+ /* NOTE: the alpha channel is the original one, so logpixel will show
+ * the original alpha but the composed color channels. This gives
+ * linear values that are apparently wrong on error, but is useful.
+ */
}
if (b->format & PNG_FORMAT_FLAG_COLOR)
@@ -1019,9 +1306,34 @@
}
}
- if (abs(a->r8-b->r8) <= error_limit &&
- abs(a->g8-b->g8) <= error_limit &&
- abs(a->b8-b->b8) <= error_limit)
+ /* Check for color-map trashing. */
+ if (b->format & PNG_FORMAT_FLAG_COLORMAP)
+ {
+ /* The data has been forced into an RGB666 colormap. Unless the
+ * original was detectably grayscale or color-mapped (we expect
+ * color maps to be preserved.)
+ */
+ if ((a->format & PNG_FORMAT_FLAG_COLOR) == 0)
+ {
+ /*TODO: grayscale input */
+ }
+
+ else if ((a->format & PNG_FORMAT_FLAG_COLORMAP) == 0)
+ {
+ /* color-map input */
+ if (error_limit < 26)
+ error_limit = 26;
+ }
+
+ else
+ {
+ /* Color-map to color-map: expect no errors. */
+ }
+ }
+
+ if (compare_8bit(a->r8, b->r8, error_limit, multiple_algorithms) &&
+ compare_8bit(a->g8, b->g8, error_limit, multiple_algorithms) &&
+ compare_8bit(a->b8, b->b8, error_limit, multiple_algorithms))
return NULL;
return err;
@@ -1056,7 +1368,7 @@
}
}
- if (abs(a->y8-b->y8) <= error_limit)
+ if (compare_8bit(a->y8, b->y8, error_limit, multiple_algorithms))
return NULL;
return err;
@@ -1118,17 +1430,44 @@
}
static int
-logpixel(Image *image, png_uint_32 x, png_uint_32 y, Pixel *a, Pixel *b,
- const char *reason)
+logpixel(Image *original, Image *copy, png_uint_32 x, png_uint_32 y, Pixel *a,
+ Pixel *b, const char *reason)
{
char pixel_a[64], pixel_b[64];
- char error_buffer[256];
print_pixel(pixel_a, a);
print_pixel(pixel_b, b);
- sprintf(error_buffer, "(%lu,%lu) %s: %s -> %s", (unsigned long)x,
- (unsigned long)y, reason, pixel_a, pixel_b);
- return logerror(image, image->file_name, error_buffer, "");
+ if (original->file_name != copy->file_name)
+ {
+ char error_buffer[256];
+ sprintf(error_buffer,
+ "(%lu,%lu) %s:\n\t%s ->\n\t\t%s\n\tUse --preserve and examine: ",
+ (unsigned long)x, (unsigned long)y, reason, pixel_a, pixel_b);
+ return logerror(original, original->file_name, error_buffer,
+ copy->file_name);
+ }
+
+ else
+ {
+ char error_buffer[256];
+ sprintf(error_buffer,
+ "(%lu,%lu) %s:\n\t%s ->\n\t\t%s.\n"
+ "\tThe error happened when reading the original file with this format",
+ (unsigned long)x, (unsigned long)y, reason, pixel_a, pixel_b);
+ return logerror(original, original->file_name, error_buffer, "");
+ }
+}
+
+static int
+badpixel(Image *ia, png_uint_32 x, png_uint_32 y, Pixel *pa, const char *reason)
+{
+ char pixel_a[64];
+ char error_buffer[128];
+
+ print_pixel(pixel_a, pa);
+ sprintf(error_buffer, "(%lu,%lu) %s: ", (unsigned long)x, (unsigned long)y,
+ reason);
+ return logerror(ia, ia->file_name, error_buffer, pixel_a);
}
/* Compare two images, the original 'a', which was written out then read back in
@@ -1146,14 +1485,15 @@
png_const_bytep rowa = a->buffer+16;
png_const_bytep rowb = b->buffer+16;
png_byte channels;
- int linear = 0;
+ int fast_track = 0;
+ int two_algorithms = ((formata ^ formatb) & PNG_FORMAT_FLAG_COLORMAP) != 0;
int result = 1;
unsigned int check_alpha = 0; /* must be zero or one */
png_byte swap_mask[4];
png_uint_32 x, y;
png_const_bytep ppa, ppb;
const png_color *background =
- ((a->opts & USE_BACKGROUND) ? &a->background : NULL);
+ ((b->opts & USE_BACKGROUND) ? &b->background : NULL);
/* This should never happen: */
if (width != b->image.width || height != b->image.height)
@@ -1161,17 +1501,13 @@
b->file_name);
/* Find the first row and inter-row space. */
- if (formata & PNG_FORMAT_FLAG_LINEAR)
- {
- stridea *= sizeof (png_uint_16);
- ++linear;
- }
+ if (!(formata & PNG_FORMAT_FLAG_COLORMAP) &&
+ (formata & PNG_FORMAT_FLAG_LINEAR))
+ stridea *= 2;
- if (formatb & PNG_FORMAT_FLAG_LINEAR)
- {
- strideb *= sizeof (png_uint_16);
- ++linear;
- }
+ if (!(formatb & PNG_FORMAT_FLAG_COLORMAP) &&
+ (formatb & PNG_FORMAT_FLAG_LINEAR))
+ strideb *= 2;
if (stridea < 0) rowa += (height-1) * (-stridea);
if (strideb < 0) rowb += (height-1) * (-strideb);
@@ -1183,9 +1519,11 @@
swap_mask[3] = swap_mask[2] = swap_mask[1] = swap_mask[0] = 0;
/* Set up the masks if no base format change, or if the format change was
- * just to add an alpha channel.
+ * just to add an alpha channel (note that this ignores whether or not the
+ * image is color-mapped.)
*/
- if (((formata | PNG_FORMAT_FLAG_ALPHA) & BASE_FORMATS) ==
+ if (((formata & BASE_FORMATS) == (formatb & BASE_FORMATS)) ||
+ ((formata | PNG_FORMAT_FLAG_ALPHA) & BASE_FORMATS) ==
(formatb & BASE_FORMATS))
{
png_byte astart = 0; /* index of first component */
@@ -1256,6 +1594,68 @@
else /* grayscale: 1 channel */
swap_mask[astart] = bstart;
+
+ /* Now work out if the fast-track match is possible - the byte
+ * representations need to be equivalent (apart from the addition of an
+ * opaque alpha channel) but allow indirection via a color-map
+ */
+ {
+ png_uint_32 f = (formata & formatb);
+
+ if (formata & PNG_FORMAT_FLAG_COLORMAP)
+ fast_track += 4; /* image a color-mapped */
+
+ if (formatb & PNG_FORMAT_FLAG_COLORMAP)
+ fast_track += 8; /* image b color-mapped */
+
+ if (fast_track == 12)
+ {
+ /* Do the color-maps match, entry by entry? Always do this the
+ * slow way unless the maps are identical, the number of entries
+ * must match.
+ */
+ unsigned int entries = a->image.colormap_entries;
+
+ if (entries == b->image.colormap_entries)
+ {
+ unsigned int entry = 0;
+
+ while (entry < entries)
+ {
+ Pixel pixel_a, pixel_b;
+ png_byte p = (png_byte)entry;
+
+ if (!get_pixel(a, &pixel_a, &p))
+ return badpixel(a, entry, 0, &pixel_a,
+ "bad palette entry value");
+
+ if (!get_pixel(b, &pixel_b, &p))
+ return badpixel(b, entry, 0, &pixel_b,
+ "bad palette entry value");
+
+ if (cmppixel(&pixel_a, &pixel_b, background, via_linear,
+ 0/*multiple_algorithms*/) != NULL)
+ break;
+
+ ++entry;
+ }
+
+ /* both sides color-mapped, color-maps match */
+ if (entry == entries)
+ fast_track += 1;
+
+ /* else color-map entries are mismatched so compare pixel by
+ * pixel.
+ */
+ }
+ }
+
+ else if (f & PNG_FORMAT_FLAG_LINEAR)
+ fast_track += 2; /* linear */
+
+ else if (!((formata | formatb) & PNG_FORMAT_FLAG_LINEAR))
+ fast_track += 3; /* sRGB */
+ }
}
ppa = rowa;
@@ -1263,9 +1663,22 @@
for (x=y=0; y<height;)
{
/* Do the fast test if possible. */
- if (channels != 0) switch (linear)
+ switch (fast_track)
{
- case 2: /* both sides linear */
+ case 8+4+1: /* both sides color-mapped and color-maps match */
+ while (x < width)
+ {
+ if (ppa[0] != ppb[0])
+ break;
+
+ /* This pixel matches, advance to the next. */
+ ++ppa;
+ ++ppb;
+ ++x;
+ }
+ break;
+
+ case 2: /* both sides double byte, neither color-mapped */
{
png_const_uint_16p lppa = (png_const_uint_16p)ppa;
png_const_uint_16p lppb = (png_const_uint_16p)ppb;
@@ -1296,7 +1709,7 @@
lppb += channels + check_alpha;
++x;
default:
- break;
+ goto linear_mismatch;
}
linear_mismatch:
@@ -1305,7 +1718,95 @@
}
break;
- case 0: /* both sides sRGB */
+ case 4+2: /* both sides double byte, imagea is color-mapped */
+ {
+ png_const_uint_16p lppb = (png_const_uint_16p)ppb;
+
+ while (x < width)
+ {
+ png_const_uint_16p lppa = ((png_const_uint_16p)a->colormap) +
+ channels * *ppa;
+
+ switch (channels)
+ {
+ case 4:
+ if (lppa[3] != lppb[swap_mask[3]])
+ goto linear_colormapa_mismatch;
+ case 3:
+ if (lppa[2] != lppb[swap_mask[2]])
+ goto linear_colormapa_mismatch;
+ case 2:
+ if (lppa[1] != lppb[swap_mask[1]])
+ goto linear_colormapa_mismatch;
+ case 1:
+ if (lppa[0] != lppb[swap_mask[0]])
+ goto linear_colormapa_mismatch;
+
+ /* The pixels apparently match, but if an alpha channel
+ * has been added (in b) it must be 65535 too.
+ */
+ if (check_alpha && 65535 != lppb[swap_mask[3]])
+ goto linear_colormapa_mismatch;
+
+ /* This pixel matches, advance to the next. */
+ ppa += 1;
+ lppb += channels + check_alpha;
+ ++x;
+ default:
+ goto linear_colormapa_mismatch;
+ }
+ }
+
+ linear_colormapa_mismatch:
+ ppb = (png_const_bytep)lppb;
+ }
+ break;
+
+ case 8+2: /* both sides double byte, imageb color-mapped */
+ {
+ png_const_uint_16p lppa = (png_const_uint_16p)ppa;
+
+ while (x < width)
+ {
+ png_const_uint_16p lppb = ((png_const_uint_16p)b->colormap) +
+ channels * *ppb;
+
+ switch (channels)
+ {
+ case 4:
+ if (lppa[3] != lppb[swap_mask[3]])
+ goto linear_colormapb_mismatch;
+ case 3:
+ if (lppa[2] != lppb[swap_mask[2]])
+ goto linear_colormapb_mismatch;
+ case 2:
+ if (lppa[1] != lppb[swap_mask[1]])
+ goto linear_colormapb_mismatch;
+ case 1:
+ if (lppa[0] != lppb[swap_mask[0]])
+ goto linear_colormapb_mismatch;
+
+ /* The pixels apparently match, but if an alpha channel
+ * has been added (in b) it must be 65535 too.
+ */
+ if (check_alpha && 65535 != lppb[swap_mask[3]])
+ goto linear_colormapb_mismatch;
+
+ /* This pixel matches, advance to the next. */
+ lppa += channels;
+ ppb += 1;
+ ++x;
+ default:
+ goto linear_colormapb_mismatch;
+ }
+ }
+
+ linear_colormapb_mismatch:
+ ppa = (png_const_bytep)lppa;
+ }
+ break;
+
+ case 3: /* both sides sRGB, neither color-mapped */
while (x < width) switch (channels)
{
case 4:
@@ -1332,12 +1833,78 @@
ppb += channels + check_alpha;
++x;
default:
- break;
+ goto sRGB_mismatch;
}
sRGB_mismatch:
break;
+ case 4+3: /* both sides sRGB, imagea color-mapped */
+ while (x < width) switch (channels)
+ {
+ case 4:
+ if (a->colormap[ppa[3]] != ppb[swap_mask[3]])
+ goto sRGB_colormapa_mismatch;
+ case 3:
+ if (a->colormap[ppa[2]] != ppb[swap_mask[2]])
+ goto sRGB_colormapa_mismatch;
+ case 2:
+ if (a->colormap[ppa[1]] != ppb[swap_mask[1]])
+ goto sRGB_colormapa_mismatch;
+ case 1:
+ if (a->colormap[ppa[0]] != ppb[swap_mask[0]])
+ goto sRGB_mismatch;
+
+ /* The pixels apparently match, but if an alpha channel has
+ * been added (in b) it must be 1.0 too.
+ */
+ if (check_alpha && 255 != ppb[swap_mask[3]])
+ goto sRGB_colormapa_mismatch;
+
+ /* This pixel matches, advance to the next. */
+ ppa += 1;
+ ppb += channels + check_alpha;
+ ++x;
+ default:
+ goto sRGB_colormapa_mismatch;
+ }
+
+ sRGB_colormapa_mismatch:
+ break;
+
+ case 8+3: /* both sides sRGB, imageb color-mapped */
+ while (x < width) switch (channels)
+ {
+ case 4:
+ if (ppa[3] != b->colormap[ppb[swap_mask[3]]])
+ goto sRGB_colormapb_mismatch;
+ case 3:
+ if (ppa[2] != b->colormap[ppb[swap_mask[2]]])
+ goto sRGB_colormapb_mismatch;
+ case 2:
+ if (ppa[1] != b->colormap[ppb[swap_mask[1]]])
+ goto sRGB_colormapb_mismatch;
+ case 1:
+ if (ppa[0] != b->colormap[ppb[swap_mask[0]]])
+ goto sRGB_colormapb_mismatch;
+
+ /* The pixels apparently match, but if an alpha channel has
+ * been added (in b) it must be 1.0 too.
+ */
+ if (check_alpha && 255 != b->colormap[ppb[swap_mask[3]]])
+ goto sRGB_colormapb_mismatch;
+
+ /* This pixel matches, advance to the next. */
+ ppa += channels;
+ ppb += 1;
+ ++x;
+ default:
+ goto sRGB_colormapb_mismatch;
+ }
+
+ sRGB_colormapb_mismatch:
+ break;
+
default: /* formats do not match */
break;
}
@@ -1350,13 +1917,18 @@
Pixel pixel_a, pixel_b;
const char *mismatch;
- get_pixel(a, &pixel_a, ppa);
- get_pixel(b, &pixel_b, ppb);
- mismatch = cmppixel(&pixel_a, &pixel_b, background, via_linear);
+ if (!get_pixel(a, &pixel_a, ppa))
+ return badpixel(a, x, y, &pixel_a, "bad pixel value");
+
+ if (!get_pixel(b, &pixel_b, ppb))
+ return badpixel(b, x, y, &pixel_b, "bad pixel value");
+
+ mismatch = cmppixel(&pixel_a, &pixel_b, background, via_linear,
+ two_algorithms);
if (mismatch != NULL)
{
- (void)logpixel(a, x, y, &pixel_a, &pixel_b, mismatch);
+ (void)logpixel(a, b, x, y, &pixel_a, &pixel_b, mismatch);
if ((a->opts & KEEP_GOING) == 0)
return 0;
@@ -1387,6 +1959,9 @@
static int
read_file(Image *image, png_uint_32 format)
{
+ memset(&image->image, 0, sizeof image->image);
+ image->image.version = PNG_IMAGE_VERSION;
+
if (image->input_memory != NULL)
{
if (!png_image_begin_read_from_memory(&image->image, image->input_memory,
@@ -1411,6 +1986,7 @@
*/
{
int result;
+ png_uint_32 image_format;
/* Various random settings for detecting overwrites */
image->background.red = 89;
@@ -1418,23 +1994,43 @@
image->background.blue = 178;
/* Print both original and output formats. */
+ image_format = image->image.format;
+
if (image->opts & VERBOSE)
printf("%s %lu x %lu %s -> %s\n", image->file_name,
(unsigned long)image->image.width,
(unsigned long)image->image.height,
- format_names[image->image.format & 0x1f],
+ format_names[image_format & FORMAT_MASK],
(format & FORMAT_NO_CHANGE) != 0 || image->image.format == format
- ? "no change" : format_names[format & 0x1f]);
+ ? "no change" : format_names[format & FORMAT_MASK]);
- if ((format & FORMAT_NO_CHANGE) == 0)
- image->image.format = format;
+ /* 'NO_CHANGE' combined with the color-map flag forces the base format
+ * flags to be set on read to ensure that the original representation is
+ * not lost in the pass through a colormap format.
+ */
+ if ((format & FORMAT_NO_CHANGE) != 0)
+ {
+ if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0 &&
+ (image_format & PNG_FORMAT_FLAG_COLORMAP) != 0)
+ format = (image_format & ~BASE_FORMATS) | (format & BASE_FORMATS);
+
+ else
+ format = image_format;
+ }
+
+ image->image.format = format;
+
+ /* Force the background if the output is colormapped and not linear */
+ if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0 &&
+ (format & PNG_FORMAT_FLAG_LINEAR) == 0)
+ image->opts |= USE_BACKGROUND;
image->stride = PNG_IMAGE_ROW_STRIDE(image->image) + image->stride_extra;
allocbuffer(image);
result = png_image_finish_read(&image->image,
(image->opts & USE_BACKGROUND) ? &image->background : NULL,
- image->buffer+16, (png_int_32)image->stride);
+ image->buffer+16, (png_int_32)image->stride, image->colormap);
checkbuffer(image, image->file_name);
@@ -1523,7 +2119,7 @@
if (f != NULL)
{
if (png_image_write_to_stdio(&image->image, f, convert_to_8bit,
- image->buffer+16, (png_int_32)image->stride))
+ image->buffer+16, (png_int_32)image->stride, image->colormap))
{
if (fflush(f) == 0)
{
@@ -1557,7 +2153,7 @@
sprintf(name, "TMP%d.png", ++counter);
if (png_image_write_to_file(&image->image, name, convert_to_8bit,
- image->buffer+16, (png_int_32)image->stride))
+ image->buffer+16, (png_int_32)image->stride, image->colormap))
{
initimage(output, image->opts, output->tmpfile_name,
image->stride_extra);
@@ -1575,13 +2171,20 @@
/* 'output' has an initialized temporary image, read this back in and compare
* this against the original: there should be no change since the original
* format was written unmodified unless 'convert_to_8bit' was specified.
+ * However, if the original image was color-mapped, a simple read will zap
+ * the linear, color and maybe alpha flags, this will cause spurious failures
+ * under some circumstances.
*/
- if (read_file(output, FORMAT_NO_CHANGE))
+ if (read_file(output, image->image.format | FORMAT_NO_CHANGE))
{
+ png_uint_32 original_format = image->image.format;
+
+ if (convert_to_8bit)
+ original_format &= ~PNG_FORMAT_FLAG_LINEAR;
+
if ((output->image.format & BASE_FORMATS) !=
- ((image->image.format & BASE_FORMATS) &
- ~(convert_to_8bit ? PNG_FORMAT_FLAG_LINEAR : 0)))
- return logerror(image, image->file_name, ": format changed on read:",
+ (original_format & BASE_FORMATS))
+ return logerror(image, image->file_name, ": format changed on read: ",
output->file_name);
return compare_two_images(image, output, 0);
@@ -1620,13 +2223,10 @@
result = 1;
for (format=0; format<64; ++format)
- if (format_isset(pf, format)
-#if 1 /* TEMPORARY: disable testing color map stuff */
- && (format & PNG_FORMAT_FLAG_COLORMAP) == 0
-#endif
- )
+ if (format_isset(pf, format))
{
resetimage(©);
+ copy.opts = opts; /* because read_file can change it */
result = read_file(©, format);
if (!result)
break;
@@ -1639,6 +2239,7 @@
/* Write the *copy* just made to a new file to make sure the write side
* works ok. Check the conversion to sRGB if the copy is linear.
*/
+ output.opts = opts;
result = write_one_file(&output, ©, 0/*convert to 8bit*/);
if (!result)
break;
@@ -1648,9 +2249,11 @@
if (!result)
break;
- if ((output.image.format & PNG_FORMAT_FLAG_LINEAR) != 0)
+ if ((format & PNG_FORMAT_FLAG_LINEAR) != 0 &&
+ (format & PNG_FORMAT_FLAG_COLORMAP) == 0)
{
/* 'output' is linear, convert to the corresponding sRGB format. */
+ output.opts = opts;
result = write_one_file(&output, ©, 1/*convert to 8bit*/);
if (!result)
break;
@@ -1728,6 +2331,7 @@
else
{
+ fflush(stdout);
fprintf(stderr, "%s: %s requires a file name argument\n",
argv[0], arg);
exit(1);
@@ -1747,6 +2351,7 @@
}
else if (arg[0] == '-')
{
+ fflush(stdout);
fprintf(stderr, "%s: unknown option: %s\n", argv[0], arg);
exit(1);
}
@@ -1795,6 +2400,7 @@
if (fclose(fsuccess) || error)
{
+ fflush(stdout);
fprintf(stderr, "%s: write failed\n", touch);
exit(1);
}
@@ -1802,6 +2408,7 @@
else
{
+ fflush(stdout);
fprintf(stderr, "%s: open failed\n", touch);
exit(1);
}