[libpng17] correct low-depth-gray code and enable tests

The low-bit-depth gray tests were disabled in prior versions of libpng because
of problems which should have been fixed by the recent changes to libpng17,
enabling the tests reveals bugs in those changes which are fixed by this commit.

Signed-off-by: John Bowler <jbowler@acm.org>
This commit is contained in:
John Bowler 2015-09-16 21:09:10 -07:00
parent bb4e79c947
commit 601ae5387e
3 changed files with 237 additions and 84 deletions

View File

@ -5661,10 +5661,13 @@ image_pixel_convert_PLTE(image_pixel *this)
/* Add an alpha channel; this will import the tRNS information because tRNS is
* not valid in an alpha image. The bit depth will invariably be set to at
* least 8 prior to 1.7.0. Palette images will be converted to alpha (using
* the above API).
* the above API). With png_set_background the alpha channel is never expanded
* but this routine is used by pngvalid to simplify code; 'for_background'
* records this.
*/
static void
image_pixel_add_alpha(image_pixel *this, const standard_display *display)
image_pixel_add_alpha(image_pixel *this, const standard_display *display,
int for_background)
{
if (this->colour_type == PNG_COLOR_TYPE_PALETTE)
image_pixel_convert_PLTE(this);
@ -5674,7 +5677,7 @@ image_pixel_add_alpha(image_pixel *this, const standard_display *display)
if (this->colour_type == PNG_COLOR_TYPE_GRAY)
{
# if PNG_LIBPNG_VER < 10700
if (this->bit_depth < 8)
if (!for_background && this->bit_depth < 8)
this->bit_depth = this->sample_depth = 8;
# endif
@ -5684,7 +5687,7 @@ image_pixel_add_alpha(image_pixel *this, const standard_display *display)
* tRNS chunk to expand at this point.
*/
# if PNG_LIBPNG_VER >= 10700
if (this->bit_depth < 8)
if (!for_background && this->bit_depth < 8)
this->bit_depth = this->sample_depth = 8;
# endif
@ -6574,7 +6577,7 @@ image_transform_png_set_tRNS_to_alpha_mod(const image_transform *this,
if (that->colour_type != PNG_COLOR_TYPE_PALETTE &&
(that->colour_type & PNG_COLOR_MASK_ALPHA) == 0)
# endif
image_pixel_add_alpha(that, &display->this);
image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
#if PNG_LIBPNG_VER < 10700
/* LIBPNG BUG: otherwise libpng still expands to 8 bits! */
@ -6638,7 +6641,7 @@ image_transform_png_set_gray_to_rgb_mod(const image_transform *this,
* doesn't do this, so we don't either.
*/
if ((that->colour_type & PNG_COLOR_MASK_COLOR) == 0 && that->have_tRNS)
image_pixel_add_alpha(that, &display->this);
image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
/* Simply expand the bit depth and alter the colour type as required. */
if (that->colour_type == PNG_COLOR_TYPE_GRAY)
@ -6702,7 +6705,7 @@ image_transform_png_set_expand_mod(const image_transform *this,
that->sample_depth = that->bit_depth = 8;
if (that->have_tRNS)
image_pixel_add_alpha(that, &display->this);
image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
this->next->mod(this->next, that, pp, display);
}
@ -6812,7 +6815,7 @@ image_transform_png_set_expand_16_mod(const image_transform *this,
image_pixel_convert_PLTE(that);
if (that->have_tRNS)
image_pixel_add_alpha(that, &display->this);
image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
if (that->bit_depth < 16)
that->sample_depth = that->bit_depth = 16;
@ -7755,7 +7758,7 @@ image_transform_png_set_background_mod(const image_transform *this,
{
/* Check for tRNS first: */
if (that->have_tRNS && that->colour_type != PNG_COLOR_TYPE_PALETTE)
image_pixel_add_alpha(that, &display->this);
image_pixel_add_alpha(that, &display->this, 1/*for background*/);
/* This is only necessary if the alpha value is less than 1. */
if (that->alphaf < 1)
@ -11109,9 +11112,9 @@ int main(int argc, char **argv)
# endif
pm.test_lbg = 0;
pm.test_lbg_gamma_threshold = 1;
pm.test_lbg_gamma_transform = 0/*PNG_LIBPNG_VER >= 10700*/;
pm.test_lbg_gamma_transform = PNG_LIBPNG_VER >= 10600;
pm.test_lbg_gamma_sbit = 1;
pm.test_lbg_gamma_composition = 0;
pm.test_lbg_gamma_composition = PNG_LIBPNG_VER >= 10700;
/* And the test encodings */
pm.encodings = test_encodings;

View File

@ -2452,16 +2452,12 @@ png_do_gamma8_up(png_transformp *transform, png_transform_controlp tc)
bits -= bit_depth;
ob = ob | (inb << bits);
if (bits == 0U)
bits = 8U, *dp++ = PNG_BYTE(ob);
else
bits -= bit_depth;
bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
}
while (sp < ep);
if (bits < 8U)
*dp++ = PNG_BYTE(ob);
UNTESTED
}
else /* 8-bit --> 8-bit */
@ -2539,8 +2535,9 @@ png_do_gamma16_up(png_transformp *transform, png_transform_controlp tc)
tc->bit_depth = bit_depth;
tc->gamma = tr->to_gamma;
/* Handle the <8 bit output case differently because there can be no alpha
* channel.
/* Handle the <8 bit output case differently, the input cannot be color (at
* present) and, if there is an alpha channel, then it is for the
* low-bit-depth gray input case and we expect the alpha to be transparent.
*/
if (bit_depth < 8U)
{
@ -2548,30 +2545,73 @@ png_do_gamma16_up(png_transformp *transform, png_transform_controlp tc)
unsigned int bits = 8U;
unsigned int ob = 0U;
debug((shifts >> 4) == 1U && shift < 16U);
debug(!tr->encode_alpha && !tr->optimize_alpha);
affirm(PNG_TC_CHANNELS(*tc) == 1U);
affirm((tc->format & PNG_FORMAT_FLAG_COLOR) == 0U);
do
if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0U)
{
unsigned int inb = *sp++ << 8; /* high bits first */
inb = png_gamma_nxmbit_correct(
(inb + *sp++) >> shift, correct, 16U-shift, bit_depth);
debug((shifts >> 4) == 1U && shift < 16U);
debug(!tr->encode_alpha && !tr->optimize_alpha);
do
{
unsigned int inb = *sp++ << 8; /* high bits first */
inb = png_gamma_nxmbit_correct(
(inb + *sp++) >> shift, correct, 16U-shift, bit_depth);
bits -= bit_depth;
ob = ob | (inb << bits);
if (bits == 0U)
bits = 8U, *dp++ = PNG_BYTE(ob);
else
bits -= bit_depth;
ob = ob | (inb << bits);
if (bits == 0U)
bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
}
while (sp < ep);
UNTESTED
}
else /* low bit GA intermediate format */
{
debug((shifts >> 8) == 1U && shift < 16U);
debug(!tr->encode_alpha && !tr->optimize_alpha);
debug(tc->transparent_alpha);
/* Gray is first then the alpha component, the alpha component is just
* mapped to 0 or 1.
*/
do
{
unsigned int gray = *sp++ << 8; /* high bits first */
unsigned int alpha;
gray += *sp++;
alpha = (*sp++ << 8);
alpha += *sp++;
if (alpha == 0U)
gray = 0U; /* will be replaced later */
else
{
gray = png_gamma_nxmbit_correct(gray >> shift, correct,
16U-shift, bit_depth);
debug(alpha == 65535U);
alpha = (1U << bit_depth)-1U;
}
bits -= bit_depth;
ob = ob | (gray << bits);
bits -= bit_depth;
ob = ob | (alpha << bits);
if (bits == 0U)
bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
}
while (sp < ep-2U);
}
while (sp < ep);
if (bits < 8U)
*dp++ = PNG_BYTE(ob);
debug(sp == ep+1U);
UNTESTED
}
else
@ -2901,14 +2941,28 @@ png_do_scale16_up(png_transformp *transform, png_transform_controlp tc)
affirm(tr->shifts != 0U/*uninitialized*/);
/* This is exactly the same as above but without the gamma correction and
* without the 8-bit target support. There can only be one channel:
* without the 8-bit target support. The code handles one or two channels,
* but the result is not a PNG format unless the number of channels is just
* 1 (grayscale).
*
* For multi-channel low bit depth the channels are packed into bytes using
* the standard PNG big-endian packing.
*/
affirm(PNG_TC_CHANNELS(*tc) == 1U);
affirm((tc->format & PNG_FORMAT_FLAG_COLOR) == 0);
/* The alpha shift is actually ignored; at present we only get here with an
* alpha channel if it is to be removed for transparent alpha processing.
*/
debug(tc->format & PNG_FORMAT_FLAG_ALPHA ?
(tr->shifts >> 8) == 1U : (tr->shifts >> 4) == 1U);
debug(tc->transparent_alpha);
tc->sp = dp;
/* This is a pure scaling operation so sBIT is not invalidated or altered. */
tc->bit_depth = bit_depth;
/* TODO: maybe do this properly and use the alpha shift, but only the top bit
* matters.
*/
{
const unsigned int shift = tr->shifts & 0xFU;
const png_uint_32 factor = tr->channel_scale[0];
@ -2925,16 +2979,12 @@ png_do_scale16_up(png_transformp *transform, png_transform_controlp tc)
bits -= bit_depth;
ob = ob | (inb << bits);
if (bits == 0U)
bits = 8U, *dp++ = PNG_BYTE(ob);
else
bits -= bit_depth;
bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
}
while (sp < ep);
if (bits < 8U)
*dp++ = PNG_BYTE(ob);
UNTESTED
}
# undef png_ptr
}
@ -3368,7 +3418,6 @@ png_init_gamma(png_transformp *transform, png_transform_controlp tc)
(void)init_gamma_sBIT(tr, tc);
tr->tr.fn = png_do_scale16_up;
tc->bit_depth = tr->to_bit_depth;
UNTESTED
}
}
@ -4176,11 +4225,13 @@ gamma_correct_background(png_transform_background *tr,
/* The background is assumed to be full precision; there is no sBIT
* information for it. The convertion converts from the current depth and
* gamma of the background to that in the transform control.
* gamma of the background to that in the transform control. It uses the
* full 16-bit precision when considering the gamma values even though this
* is probably spurious.
*/
if (correction != 0 && (tr->background_gamma == 0 ||
png_gamma_equal(png_ptr, tr->background_gamma, correction, &correction,
bdback)))
16U)))
correction = 0; /* no correction! */
if (tr->background_is_gray)
@ -4197,9 +4248,8 @@ gamma_correct_background(png_transform_background *tr,
correction, bdrow);
}
if (correction != 0)
tr->background_gamma = tc->gamma;
/* Regardless of whether there was a correction set the background gamma: */
tr->background_gamma = tc->gamma;
tr->background_bit_depth = png_check_bits(png_ptr, bdrow, 5U);
# undef png_ptr
}
@ -4211,7 +4261,7 @@ fill_background_pixel(png_transform_background *tr, png_transform_controlp tc)
/* Fill in 'background_pixel' if the appropriate sequence of bytes for the
* format given in the transform control.
*/
const unsigned int bdtc = tc->bit_depth;
unsigned int bdtc = tc->bit_depth;
/* If necessary adjust the background pixel to the current row format (it is
* important to do this as late as possible to avoid spurious
@ -4225,8 +4275,15 @@ fill_background_pixel(png_transform_background *tr, png_transform_controlp tc)
/* 'g' now has enough bits for the destination, note that in the case of
* low bit depth gray this causes the pixel to be replicated through the
* written byte. Fill all six bytes:
* written byte. Fill all six bytes with the replicated background:
*/
while (bdtc < 8U)
{
g &= (1U << bdtc) - 1U; /* use only the low bits */
g |= g << bdtc;
bdtc <<= 1;
}
memset(tr->background_pixel, PNG_BYTE(g), 6U);
if (bdtc == 16U)
tr->background_pixel[0] = tr->background_pixel[2] =
@ -4409,9 +4466,6 @@ png_do_set_row(png_transformp *transform, png_transform_controlp tc)
tc->sp = dp;
memset(dp, (*transform)->args >> 24, PNG_TC_ROWBYTES(*tc));
# define png_ptr (tc->png_ptr)
UNTESTED
# undef png_ptr
}
static void
@ -4431,8 +4485,9 @@ png_do_replace_tRNS_lbd(png_transformp *transform, png_transform_controlp tc)
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
const unsigned int transparent_pixel = tr->transparent_pixel[0];
const unsigned int background_pixel = tr->background_pixel[0];
const unsigned int copy = sp != dp;
const png_byte transparent_pixel = tr->transparent_pixel[0];
const png_byte background_pixel = tr->background_pixel[0];
/* We expect opaque and transparent pixels to be interleaved but with long
* sequences of each.
@ -4470,19 +4525,28 @@ png_do_replace_tRNS_lbd(png_transformp *transform, png_transform_controlp tc)
*/
if (tc->bit_depth == 4U)
{
/* For the moment the algorithm isn't used; there are only two pixels in
* each byte so it is likely to be quicker to check as below:
*/
do
{
unsigned int b = *sp++;
unsigned int m = b;
const png_byte b = *sp++;
const unsigned int m = b ^ transparent_pixel;
m ^= transparent_pixel; /* Set transparent pixels to 0 */
m = 0x88U & ((m - 0x11U) & ~m); /* Top bit set for transparent pixel */
m |= m >> 1; /* Set all four bits */
m |= m >> 2;
if (m == 0U) /* both transparent */
*dp = background_pixel;
*dp++ = PNG_BYTE((b & ~m) | (background_pixel & m));
else if ((m & 0xF0U) == 0U) /* first transparent */
*dp = PNG_BYTE((background_pixel & 0xF0U) | (b & 0x0FU));
else if ((m & 0x0FU) == 0U) /* second transparent */
*dp = PNG_BYTE((background_pixel & 0x0FU) | (b & 0xF0U));
else if (copy) /* neither transparent */
*dp = b;
++dp;
} while (sp < ep);
UNTESTED
}
else
@ -4491,19 +4555,31 @@ png_do_replace_tRNS_lbd(png_transformp *transform, png_transform_controlp tc)
do
{
unsigned int b = *sp++;
unsigned int m = b;
const png_byte b = *sp++;
const unsigned int m = b ^ transparent_pixel;
m ^= transparent_pixel; /* Set transparent pixels to 0 */
m = 0xCCU & ((m - 0x55U) & ~m); /* Top bit set for transparent pixel */
m |= m >> 1; /* Set both bits */
if (m == 0U) /* transparent */
*dp = background_pixel;
*dp++ = PNG_BYTE((b & ~m) | (background_pixel & m));
else if (0xAAU & ((m - 0x55U) & ~m))
{
/* One or more pixels transparent */
const unsigned int mask =
(m & 0xC0U ? 0xC0U : 0U) |
(m & 0x30U ? 0x30U : 0U) |
(m & 0x0CU ? 0x0CU : 0U) |
(m & 0x03U ? 0x03U : 0U);
*dp = PNG_BYTE((b & mask) | (background_pixel & ~mask));
}
else if (copy) /* no transparent pixels */
*dp = b;
++dp;
} while (sp < ep);
UNTESTED
}
UNTESTED
# undef png_ptr
}
@ -4572,6 +4648,76 @@ png_do_background_with_transparent_GA16(png_transformp *transform,
# undef png_ptr
}
static void
png_do_background_with_transparent_GAlbd(png_transformp *transform,
png_transform_controlp tc)
/* This is the low-bit-depth gray case, the input is 1, 2 or 4-bit per
* channel gray-alpha.
*/
{
# define png_ptr (tc->png_ptr)
png_transform_background *tr =
png_transform_cast(png_transform_background, *transform);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
const unsigned int bit_depth = tc->bit_depth;
const unsigned int mask = (1U << bit_depth) - 1U;
const unsigned int back = tr->background_pixel[0] & mask;
unsigned int opos, ob, inb;
debug(bit_depth < 8U && tc->format == PNG_FORMAT_GA && tr->ntrans == 1U);
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
tc->sp = dp;
ob = 0U; /* output byte */
opos = 0U; /* bit index of previous output pixel (counts down) */
inb = 0U; /* quiet a GCC 4.8.5 warning */
for (;;)
{
/* The output is half the size of the input, so we need a new input byte
* for every 4 bits of output:
*/
if (opos == 0U || opos == 4U)
{
if (sp >= ep)
break;
inb = *sp++;
}
/* Move to the next *output* pixel, this wraps when bits is 0U: */
opos = (opos - bit_depth) & 0x7U;
/* Extract the whole input pixel to the low bits of a temporary: */
{
unsigned int pixel = inb >> ((opos*2U) & 0x7U);
/* The alpha channel is second, check for a value of 0: */
if ((pixel & mask)/* A component*/ == 0U)
pixel = back;
else
{
debug((pixel & mask) == mask);
pixel = (pixel >> bit_depth) & mask; /* G component */
}
ob |= pixel << opos;
}
if (opos == 0U)
*dp++ = PNG_BYTE(ob), ob = 0U;
}
if (opos != 0U)
*dp++ = PNG_BYTE(ob);
debug(sp == ep);
# undef png_ptr
}
static void
png_do_background_with_transparent_RGBA8(png_transformp *transform,
png_transform_controlp tc)
@ -4662,11 +4808,11 @@ png_init_background_transparent(png_transformp *transform,
if (tc->bit_depth == 8U)
tr->tr.fn = png_do_background_with_transparent_GA8;
else
{
debug(tc->bit_depth == 16U);
else if (tc->bit_depth == 16U)
tr->tr.fn = png_do_background_with_transparent_GA16;
}
else /* low-bit-depth gray with alpha (not a PNG format!) */
tr->tr.fn = png_do_background_with_transparent_GAlbd;
}
else /* color */
@ -5070,7 +5216,14 @@ png_init_background(png_transformp *transform, png_transform_controlp tc)
/* tRNS will be expanded, or handled */
tc->invalid_info |= PNG_INFO_tRNS;
if (!tr->compose_background)
{
tc->format |= PNG_FORMAT_FLAG_ALPHA;
/* And in this case, only, because we are adding an alpha channel we
* need to have a channel depth of at least 8:
*/
if (tc->bit_depth < 8U)
tc->bit_depth = 8U;
}
}
else /* no transparent pixels to change */
@ -5147,6 +5300,7 @@ png_init_background(png_transformp *transform, png_transform_controlp tc)
/* TODO: it may be impossible to get here! */
affirm(tc->transparent_alpha);
/* This init routine does the sBIT handling: */
UNTESTED
png_init_background_transparent(transform, tc);
UNTESTED
}
@ -5176,13 +5330,14 @@ png_init_background(png_transformp *transform, png_transform_controlp tc)
*
* NOTE: for palette images this test happens in the caching
* operation, so the answer is still correct.
*
* NOTE: for low bit depth gray both 'transparent_pixel' and
* 'background_pixel' have been expanded to fill a byte, so this
* works.
*/
if (memcmp(tr->transparent_pixel, tr->background_pixel, tr->ntrans)
== 0)
{
tr->tr.fn = NULL;
UNTESTED
}
/* Then the processing function depends on the pixel size: */
else if (tr->ntrans > 1U)
@ -5204,14 +5359,10 @@ png_init_background(png_transformp *transform, png_transform_controlp tc)
args |= PNG_INFO_tRNS | PNG_INFO_sRGB;
tr->tr.args = args;
tr->tr.fn = png_do_set_row;
UNTESTED
}
else
{
tr->tr.fn = png_do_replace_tRNS_lbd;
UNTESTED
}
tc->invalid_info |= PNG_INFO_tRNS | PNG_INFO_sBIT;
tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
@ -6061,8 +6212,8 @@ make_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth)
/* At present the cache is just a byte lookup table. We need the original
* pixel depth to work out how big the working buffer needs to be.
*/
unsigned int ipd = PNG_TC_PIXEL_DEPTH(cp->tstart);
unsigned int opd = PNG_TC_PIXEL_DEPTH(cp->tend);
const unsigned int ipd = PNG_TC_PIXEL_DEPTH(cp->tstart);
const unsigned int opd = PNG_TC_PIXEL_DEPTH(cp->tend);
unsigned int order; /* records position of start transform */
unsigned int width; /* width of cache in pixels */
png_tc_channel_data save; /* Record of the final channel info */
@ -6075,6 +6226,7 @@ make_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth)
debug(cp->tend.init == PNG_TC_INIT_FINAL);
affirm(opd <= 64 && max_depth <= 64); /* or the cache is not big enough */
affirm(ipd == opd || (opd & 0x7U) == 0);
if ((cp->tstart.format & PNG_FORMAT_FLAG_COLORMAP) != 0)
width = setup_palette_cache(png_ptr, cache.b8);
@ -6244,8 +6396,6 @@ make_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth)
*/
png_transform_fn fn;
affirm((opd & 7U) == 0); /* must be whole bytes */
# define C(ipd,opd) ((ipd) + 8*(opd))
switch (C(ipd,opd))
{

View File

@ -399,7 +399,7 @@ png_push_transform(png_structrp png_ptr, size_t size, png_transform_fn fn,
}
while (tr != NULL && tr->order == old_order);
affirm(tr->order > old_order);
affirm(tr == NULL || tr->order > old_order);
}
*transform = png_add_transform(png_ptr, size, fn, order);