blob: 3b627498324e3a017ede897b4d8e7fc5a7e2b86b [file] [log] [blame]
/* pngtrans.c - transforms the data in a row (used by both readers and writers)
*
* Last changed in libpng 1.7.0 [(PENDING RELEASE)]
* Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
#include "pngpriv.h"
#define PNG_SRC_FILE PNG_SRC_FILE_pngtrans
#ifdef _XOPEN_SOURCE
# include <unistd.h>
#endif /* for swab */
/* Memory format enquiries */
#ifdef PNG_GAMMA_SUPPORTED
static png_fixed_point
memory_gamma(png_const_structrp png_ptr)
{
# ifdef PNG_READ_GAMMA_SUPPORTED
# ifdef PNG_TRANSFORM_MECH_SUPPORTED
if (png_ptr->read_struct)
return png_ptr->row_gamma;
# endif /* TRANSFORM_MECH */
# endif /* READ_GAMMA */
/* Else either no READ_GAMMA support or this is a write struct; in both
* cases there are no gamma transforms. In the write case the set of the
* gamma in the info may not have been copied to the png_struct.
*/
# if defined(PNG_GAMMA_SUPPORTED) && defined(PNG_READ_SUPPORTED)
if ((png_ptr->colorspace.flags &
(PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) ==
PNG_COLORSPACE_HAVE_GAMMA)
return png_ptr->colorspace.gamma;
# else /* !(GAMMA && READ) */
PNG_UNUSED(png_ptr)
# endif /* !(GAMMA && READ) */
/* '0' means the value is not know: */
return 0;
}
#endif /* GAMMA */
unsigned int PNGAPI
png_memory_format(png_structrp png_ptr)
{
/* The in-memory format as a bitmask of PNG_FORMAT_FLAG_ values. All the
* flags listed below are used. If PNG_FORMAT_FLAG_INVALID is set the
* following caveats apply to the interpretation of PNG_FORMAT_FLAG_LINEAR:
*
* The gamma may differ from the sRGB (!LINEAR) or 1.0 (LINEAR). Call
* png_memory_gamma to find the correct value.
*
* The channel depth may differ from 8 (!LINEAR) or 16 (LINEAR). Call
* png_memory_channel_depth to find the correct value.
*
* It is only valid to call these APIS *after* either png_read_update_info
* or png_start_read_image on read or after the first row of an image has
* been written on write.
*/
if (png_ptr != NULL)
{
# ifdef PNG_TRANSFORM_MECH_SUPPORTED
unsigned int format = png_ptr->row_format;
# else /* !TRANSFORM_MECH */
unsigned int format = PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type);
# endif /* !TRANSFORM_MECH */
if (png_ptr->read_struct) /* else no way to find the gamma! */
{
# ifdef PNG_GAMMA_SUPPORTED
# ifdef PNG_TRANSFORM_MECH_SUPPORTED
unsigned int bit_depth = png_ptr->row_bit_depth;
# else /* !TRANSFORM_MECH */
unsigned int bit_depth = png_ptr->bit_depth;
# endif /* !TRANSFORM_MECH */
/* Now work out whether this is a valid simplified API format. */
switch (bit_depth)
{
case 8U:
{
png_fixed_point gamma = memory_gamma(png_ptr);
if (!PNG_GAMMA_IS_sRGB(gamma))
format |= PNG_FORMAT_FLAG_INVALID;
}
break;
case 16:
if (memory_gamma(png_ptr) == PNG_GAMMA_LINEAR)
{
static const union
{
png_uint_16 u16;
png_byte u8[2];
} sex = { 1U };
format |= PNG_FORMAT_FLAG_LINEAR;
/* But the memory layout of the 16-bit quantities must also
* match; we need swapped data on LSB platforms.
*/
if (sex.u8[0] == ((format & PNG_FORMAT_FLAG_SWAPPED) != 0))
break; /* ok */
}
/* FALL THROUGH*/
default: /* bit depth not supported for simplified API */
format |= PNG_FORMAT_FLAG_INVALID;
break;
}
# else /* !GAMMA */
/* We have no way of knowing if the gamma value matches that
* expected by the simplified API so mark the format as invalid:
*/
format |= PNG_FORMAT_FLAG_INVALID;
# endif
} /* read_struct */
return format;
}
return 0;
}
unsigned int PNGAPI png_memory_channel_depth(png_structrp png_ptr)
{
/* The actual depth of each channel in the image. */
if (png_ptr != NULL)
{
# ifdef PNG_TRANSFORM_MECH_SUPPORTED
return png_ptr->row_bit_depth;
# else
return png_ptr->bit_depth;
# endif
}
return 0;
}
#ifdef PNG_GAMMA_SUPPORTED
png_fixed_point PNGAPI
png_memory_gamma(png_structrp png_ptr)
{
/* The actual gamma of the image data, scaled by 100,000. This is the
* encoding gamma, e.g. 1/2.2 for sRGB. If the gamma is unknown this will
* return 0.
*
* On write this invariably returns 0; libpng does not change the gamma of
* the data on write.
*
* Note that this is not always the exact inverse of the 'screen gamma'
* passed to png_set_gamma; internal optimizations remove attempts to make
* small changes to the gamma value. This function returns the actual
* output value.
*/
return (png_ptr != NULL) ? memory_gamma(png_ptr) : 0;
}
#endif /* GAMMA */
/* These are general purpose APIs that deal with the row buffer format in both
* the read and write case. The png_struct::row_* members describe the
* in-memory format of the image data based on the transformations requested by
* the application.
*/
#ifdef PNG_TRANSFORM_MECH_SUPPORTED
png_voidp /* PRIVATE */
png_transform_cast_check(png_const_structp png_ptr, unsigned int src_line,
png_transformp tr, size_t size)
{
/* Given a pointer to a transform, 'tr' validate that the underlying derived
* class has size 'size' using the tr->size field and return the same
* pointer. If there is a size mismatch the function does an affirm using
* the given line number.
*/
if (tr->size != size)
png_affirm(png_ptr, param_deb("transform upcast") src_line);
return tr;
}
void /* PRIAVE */
png_transform_free(png_const_structrp png_ptr, png_transformp *list)
{
if (*list != NULL)
{
png_transform_free(png_ptr, &(*list)-> next);
if ((*list)->free != NULL)
(*list)->free(png_ptr, *list);
png_free(png_ptr, *list);
*list = NULL;
}
}
/* Utility to initialize a png_transform_control for read or write. */
void /* PRIVATE */
png_init_transform_control(png_transform_controlp tc, png_structp png_ptr)
{
png_byte bd; /* bit depth of the row */
png_byte cd; /* bit depth of color information */
memset(tc, 0, sizeof *tc);
tc->png_ptr = png_ptr; /* ALIAS */
tc->sp = tc->dp = NULL;
tc->width = 0;
# ifdef PNG_READ_GAMMA_SUPPORTED
/* The file gamma is set by png_set_gamma, as well as being read from the
* input PNG gAMA chunk, if present, we don't have a png_info so can't get
* the set_gAMA value but this doesn't matter because on read the gamma
* value is in png_struct::colorspace and on write it isn't used.
*/
if ((png_ptr->colorspace.flags &
(PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) ==
PNG_COLORSPACE_HAVE_GAMMA)
{
tc->gamma = png_ptr->colorspace.gamma;
debug(tc->gamma > 0);
}
else
{
/* There is no input gamma, so there should be no overall gamma
* correction going on. This test works because the various things
* that set an output gamma also default the input gamma.
*/
debug(png_ptr->row_gamma == 0);
}
# endif
/* Validate bit depth and color type here */
cd = bd = png_ptr->bit_depth;
switch (png_ptr->color_type)
{
case PNG_COLOR_TYPE_GRAY:
affirm(bd == 1U || bd == 2U || bd == 4U || bd == 8U || bd == 16U);
tc->format = 0U;
break;
case PNG_COLOR_TYPE_PALETTE:
affirm(bd == 1U || bd == 2U || bd == 4U || bd == 8U);
tc->format = PNG_FORMAT_FLAG_COLORMAP | PNG_FORMAT_FLAG_COLOR;
cd = 8U;
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
affirm(bd == 8U || bd == 16U);
tc->format = PNG_FORMAT_FLAG_ALPHA;
break;
case PNG_COLOR_TYPE_RGB:
affirm(bd == 8U || bd == 16U);
tc->format = PNG_FORMAT_FLAG_COLOR;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
affirm(bd == 8U || bd == 16U);
tc->format = PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_ALPHA;
break;
default:
impossible("PNG color type");
}
tc->bit_depth = bd;
tc->range = 0;
/* Preset the sBIT data to full precision/handled. */
tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = cd;
# ifdef PNG_READ_sBIT_SUPPORTED
{
int handled = 1;
if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
{
png_byte c = png_ptr->sig_bit.red;
if (c > 0 && c < cd)
{
tc->sBIT_R = c;
handled = 0;
}
c = png_ptr->sig_bit.green;
if (c > 0 && c < cd)
{
tc->sBIT_G = c;
handled = 0;
}
c = png_ptr->sig_bit.blue;
if (c > 0 && c < cd)
{
tc->sBIT_B = c;
handled = 0;
}
}
else /* grayscale */
{
png_byte c = png_ptr->sig_bit.gray;
if (c > 0 && c < cd)
{
tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = c;
handled = 0;
}
}
/* The palette-mapped format doesn't store alpha information, an
* omission in the spec that is difficult to fix. Notice that
* 'handled' is not cleared below, this is because the alpha channel is
* always linear, so the sBIT_A value can always be treated as a
* precision value.
*/
if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
{
png_byte c = png_ptr->sig_bit.alpha;
if (c > 0 && c < cd)
tc->sBIT_A = c;
}
/* If 'handled' did not get cleared there is no sBIT information. */
if (handled)
tc->invalid_info = PNG_INFO_sBIT;
}
# else /* !READ_sBIT */
/* No sBIT information */
tc->invalid_info = PNG_INFO_sBIT;
# endif /* !READ_sBIT */
}
png_transformp /*PRIVATE*/
png_add_transform(png_structrp png_ptr, size_t size, png_transform_fn fn,
unsigned int order)
{
/* Add a transform. This is a minimal implementation; the order is just
* controlled by 'order', the result is a point to the new transform, or
* to an existing one if one was already in the list.
*/
png_transformp *p = &png_ptr->transform_list;
while (*p != NULL && (*p)->order < order)
p = &(*p)->next;
if (size == 0)
size = sizeof (png_transform);
else
affirm(size >= sizeof (png_transform));
if (*p == NULL || (*p)->order > order)
{
png_transformp t;
t = png_voidcast(png_transformp, png_malloc(png_ptr, size));
memset(t, 0, size); /* zeros out the extra data too */
/* *p comes after the new entry, t: */
t->next = *p;
t->fn = fn;
t->free = NULL;
t->order = order;
t->size = 0xFFFFU & size;
*p = t;
return t;
}
else /* (*p)->order matches order, return *p */
{
affirm((*p)->fn == fn && (*p)->order == order && (*p)->size == size);
return *p;
}
}
png_transformp /* PRIVATE */
png_push_transform(png_structrp png_ptr, size_t size, png_transform_fn fn,
png_transformp *transform, png_transform_controlp tc)
{
png_transformp tr = *transform;
unsigned int order = tr->order;
/* Basic loop detection: */
affirm(fn != NULL && tr->fn != fn);
/* Move the following transforms up: */
{
unsigned int old_order = order;
do
{
tr->order = ++old_order;
tr = tr->next;
}
while (tr != NULL && tr->order == old_order);
affirm(tr == NULL || tr->order > old_order);
}
*transform = png_add_transform(png_ptr, size, fn, order);
if (tc != NULL)
fn(transform, tc);
return *transform;
}
#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
static png_transformp
png_find_transform(png_const_structrp png_ptr, unsigned int order)
/* Find a transform with the given order, or return NULL. Currently only
* used here.
*/
{
png_transformp p = png_ptr->transform_list;
for (;;)
{
if (p == NULL || p->order > order)
return NULL;
if (p->order == order)
return p;
p = p->next;
}
}
#endif /* USER_TRANSFORM_PTR */
static void
remove_transform(png_const_structp png_ptr, png_transformp *transform)
/* Remove a transform on a running list */
{
png_transformp tp = *transform;
png_transformp next = tp->next;
*transform = next;
tp->next = NULL;
png_transform_free(png_ptr, &tp);
}
#ifdef PNG_READ_TRANSFORMS_SUPPORTED
void /* PRIVATE */
png_remove_transform(png_const_structp png_ptr, png_transformp *transform)
{
remove_transform(png_ptr, transform);
}
#endif /* READ_TRANSFORMS */
static unsigned int
run_transform_list_forwards(png_transform_controlp tc, png_transformp *start,
png_transformp end/*NULL for whole list*/)
/* Called from the init code and below, the caller must initialize 'tc' */
{
png_const_structp png_ptr = tc->png_ptr;
unsigned int max_depth = PNG_TC_PIXEL_DEPTH(*tc);
/* Caller guarantees that *start is non-NULL */
debug(*start != NULL);
do
{
if ((*start)->fn != NULL)
(*start)->fn(start, tc);
if ((*start)->fn == NULL) /* delete this transform */
remove_transform(png_ptr, start);
else
{
/* Handle the initialization of the maximum pixel depth. */
unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(*tc);
if (tc_depth > max_depth)
max_depth = tc_depth;
/* Advance to the next transform. */
start = &(*start)->next;
}
}
while (*start != NULL && *start != end);
/* This only goes wrong if 'end' was non-NULL and not in the list: */
debug(*start == end);
return max_depth;
}
#ifdef PNG_READ_TRANSFORMS_SUPPORTED
unsigned int /* PRIVATE */
png_run_this_transform_list_forwards(png_transform_controlp tc,
png_transformp *start, png_transformp end)
{
return run_transform_list_forwards(tc, start, end);
}
#endif /* READ_TRANSFORMS */
#ifdef PNG_READ_SUPPORTED
unsigned int /* PRIVATE */
png_run_transform_list_forwards(png_structp png_ptr, png_transform_controlp tc)
{
if (png_ptr->transform_list != NULL)
return run_transform_list_forwards(tc, &png_ptr->transform_list, NULL);
else
return PNG_PIXEL_DEPTH(*png_ptr);
}
#endif /* READ */
#ifdef PNG_WRITE_SUPPORTED /* only used from pngwrite.c */
static unsigned int
run_transform_list_backwards(png_transform_controlp tc, png_transformp *list)
{
png_const_structp png_ptr = tc->png_ptr;
unsigned int max_depth = 0;
if ((*list)->next != NULL)
max_depth = run_transform_list_backwards(tc, &(*list)->next);
/* Note that the above might change (*list)->next, but it can't change
* *list itself.
*/
if ((*list)->fn != NULL)
(*list)->fn(list, tc);
/* If that set 'fn' to NULL this transform must be removed; this is how
* (*list)->next gets changed in our caller:
*/
if ((*list)->fn == NULL)
remove_transform(png_ptr, list);
else
{
unsigned int depth = PNG_TC_PIXEL_DEPTH(*tc);
if (depth > max_depth)
max_depth = depth;
}
return max_depth;
}
void /* PRIVATE */
png_run_transform_list_backwards(png_structp png_ptr, png_transform_controlp tc)
{
if (png_ptr->transform_list != NULL)
{
/* This doesn't take account of the base PNG depth, but that shouldn't
* matter, it's just a check:
*/
unsigned int max_depth =
run_transform_list_backwards(tc, &png_ptr->transform_list);
/* Better late than never (if this fires a memory overwrite has happened):
*/
affirm(max_depth <= png_ptr->row_max_pixel_depth);
}
}
#endif /* WRITE */
static unsigned int
init_transform_mech(png_structrp png_ptr, png_transform_control *tc, int start)
/* Called each time to run the transform list once during initialization. */
{
png_init_transform_control(tc, png_ptr);
tc->init = start ? PNG_TC_INIT_FORMAT : PNG_TC_INIT_FINAL;
# ifdef PNG_READ_TRANSFORMS_SUPPORTED
if (png_ptr->read_struct)
return png_read_init_transform_mech(png_ptr, tc);
else
# endif
return run_transform_list_forwards(tc, &png_ptr->transform_list, NULL);
}
#endif /* TRANSFORM_MECH */
#ifdef PNG_PALETTE_MAX_SUPPORTED
static int
set_palette_max(png_structrp png_ptr, png_transformp tr, unsigned int max,
unsigned int format_max)
/* Called whenever a new maximum pixel value is found */
{
/* One of these must be true: */
# ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
if (max >= (tr->args & 0x1FFU) && !png_ptr->palette_index_check_issued)
{
/* In 1.7 only issue the error/warning by default; the 'check' API is
* used to enable/disable the check. Assume that if the app enabled it
* then the app will be checking the result with get_palette_max in
* read. In write an error results unless the check is disabled.
*/
if (png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT
# ifdef PNG_WRITE_SUPPORTED
|| (!png_ptr->read_struct &&
png_ptr->palette_index_check != PNG_PALETTE_CHECK_OFF)
# endif /* WRITE */
)
{
# ifdef PNG_READ_SUPPORTED
# ifdef PNG_WRITE_SUPPORTED
if (png_ptr->read_struct)
# endif /* WRITE */
png_chunk_benign_error(png_ptr, "palette index too large");
# ifdef PNG_WRITE_SUPPORTED
else
# endif
# endif /* READ */
# ifdef PNG_WRITE_SUPPORTED
png_error(png_ptr, "palette index too large");
# endif /* WRITE */
}
png_ptr->palette_index_check_issued = 1;
}
# endif /* CHECK_FOR_INVALID_INDEX */
# ifdef PNG_GET_PALETTE_MAX_SUPPORTED
png_ptr->palette_index_max = png_check_byte(png_ptr, max);
# endif
if (max == format_max)
{
tr->fn = NULL; /* no point continuing once the max has been seen */
return 1; /* stop */
}
return 0; /* keep going */
}
static void
palette_max_1bpp(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_uint_32 width = tc->width;
while (width >= 8)
{
if (*sp++) break;
width -= 8;
}
if (width < 8)
{
if (width == 0 ||
(*sp & (((1U<<width)-1U) << (8-width))) == 0)
return; /* no '1' pixels */
}
/* If the code reaches this point there is a set pixel */
(void)set_palette_max(tc->png_ptr, *tr, 1U, 1U);
}
static void
palette_max_2bpp(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_uint_32 width = tc->width;
const png_uint_32 args = (*tr)->args;
unsigned int max = args >> 24; /* saved maximum */
while (width > 0)
{
png_uint_32 input = 0U, test;
unsigned int new_max;
/* This just skips 0 bytes: */
while (width > 0)
{
unsigned int next = *sp++;
/* There may be partial pixels at the end, just remove the absent
* pixels with a right shift:
*/
if (width >= 4)
width -= 4;
else
next >>= (4U-width) * 2U, width = 0;
if (next)
{
input = (input << 8) | next;
if ((input & 0xFF000000U) != 0)
break;
}
}
test = input & 0xAAAAAAAAU;
if (test != 0)
{
if ((input & (test >> 1)) != 0)
new_max = 3U; /* both bits set in at least one pixel */
else if (max < 2U)
new_max = 2U;
else
continue; /* no change to max */
}
else /* test is 0 */ if (input != 0 && max == 0)
new_max = 1U;
else /* input is 0, or max is at least 1 */
continue;
/* new_max is greater than max: */
if (set_palette_max(tc->png_ptr, *tr, new_max, 3U))
return;
/* Record new_max: */
max = new_max;
}
/* End of input, check the next line. */
(*tr)->args = (max << 24) + (args & 0xFFFFFFU);
}
static void
palette_max_4bpp(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_uint_32 width = tc->width;
const png_uint_32 args = (*tr)->args;
unsigned int max = args >> 24; /* saved maximum */
while (width > 0)
{
unsigned int input = *sp++;
if (width >= 2)
width -= 2;
else
input >>= 1, width = 0;
if ((input & 0xFU) > max)
max = input & 0xFU;
if (((input >> 4) & 0xFU) > max)
max = (input >> 4) & 0xFU;
}
if (max > (args >> 24))
{
if (set_palette_max(tc->png_ptr, *tr, max, 15U))
return;
(*tr)->args = (max << 24) + (args & 0xFFFFFFU);
}
}
static void
palette_max_8bpp(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_uint_32 width = tc->width;
const png_uint_32 args = (*tr)->args;
unsigned int max = args >> 24; /* saved maximum */
while (width > 0)
{
unsigned int input = *sp++;
if (input > max)
max = input;
--width;
}
if (max > (args >> 24))
{
if (set_palette_max(tc->png_ptr, *tr, max, 255U))
return;
(*tr)->args = (max << 24) + (args & 0xFFFFFFU);
}
}
static void
palette_max_init(png_transformp *tr, png_transform_controlp tc)
{
# define png_ptr (tc->png_ptr)
affirm((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0);
debug(tc->init);
if (tc->init == PNG_TC_INIT_FINAL)
{
/* Record the palette depth to check here along with the running total
* in the top 8 bits (initially 0, which is always valid).
*/
(*tr)->args = png_ptr->num_palette;
switch (tc->bit_depth)
{
case 1: (*tr)->fn = palette_max_1bpp; break;
case 2: (*tr)->fn = palette_max_2bpp; break;
case 4: (*tr)->fn = palette_max_4bpp; break;
case 8: (*tr)->fn = palette_max_8bpp; break;
default:impossible("palette bit depth");
}
png_ptr->palette_index_have_max = 1U;
}
# undef png_ptr
}
#endif /* PALETTE_MAX */
#ifdef PNG_GET_PALETTE_MAX_SUPPORTED
int PNGAPI
png_get_palette_max(png_const_structrp png_ptr, png_const_inforp info_ptr)
{
if (png_ptr != NULL && png_ptr->palette_index_have_max)
return png_ptr->palette_index_max;
/* This indicates to the caller that the information is not available: */
return -1;
PNG_UNUSED(info_ptr)
}
#endif /* GET_PALETTE_MAX */
void /* PRIVATE */
png_init_row_info(png_structrp png_ptr)
{
/* PNG pixels never exceed 64 bits in depth: */
const png_byte png_depth =
png_check_bits(png_ptr, PNG_PIXEL_DEPTH(*png_ptr), 7U);
# ifdef PNG_TRANSFORM_MECH_SUPPORTED
/* The palette index check stuff is *on* automatically. To handle this
* add it here, if it is supported.
*/
# ifdef PNG_PALETTE_MAX_SUPPORTED
/* The logic here is a little complex because of the plethora of
* #defines controlling this stuff.
*/
if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE/* fast escape */ && (
# if defined (PNG_READ_GET_PALETTE_MAX_SUPPORTED) ||\
defined (PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED)
(png_ptr->read_struct
# ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
&& (png_ptr->palette_index_check == PNG_PALETTE_CHECK_ON ||
(png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT
&& png_ptr->num_palette < (1U << png_ptr->bit_depth)))
# endif /* READ_CHECK_FOR_INVALID_INDEX */
)
# else /* no READ support */
0
# endif /* READ checks */
||
# if defined (PNG_WRITE_GET_PALETTE_MAX_SUPPORTED) ||\
defined (PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
(!png_ptr->read_struct
# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
&& (png_ptr->palette_index_check == PNG_PALETTE_CHECK_ON ||
(png_ptr->palette_index_check == PNG_PALETTE_CHECK_DEFAULT
&& png_ptr->num_palette < (1U << png_ptr->bit_depth)))
# endif /* WRITE_CHECK_FOR_INVALID_INDEX */
)
# else /* no WRITE support */
0
# endif /* WRITE checks */
))
png_add_transform(png_ptr, 0/*size*/, palette_max_init,
PNG_TR_CHECK_PALETTE);
# endif
/* Application transforms may change the format of the data or, when
* producing interlaced images, the number of pixels in a line. This code
* determines the maximum pixel depth required and allows transformations
* a chance to initialize themselves.
*/
if (png_ptr->transform_list != NULL)
{
png_transform_control tc;
(void)init_transform_mech(png_ptr, &tc, 1/*start*/);
png_ptr->row_format = png_check_bits(png_ptr, tc.format, PNG_RF_BITS);
affirm(tc.bit_depth <= 32);
png_ptr->row_bit_depth = png_check_bits(png_ptr, tc.bit_depth, 6);
png_ptr->row_range = png_check_bits(png_ptr, tc.range, 3);
# ifdef PNG_READ_GAMMA_SUPPORTED
png_ptr->row_gamma = tc.gamma;
# endif /* READ_GAMMA */
/* The above may have cancelled all the transforms in the list. */
if (png_ptr->transform_list != NULL)
{
/* Run the transform list again, also forward, and accumulate the
* maximum pixel depth. At this point the transforms can swap
* out their initialization code.
*/
unsigned int max_depth =
init_transform_mech(png_ptr, &tc, 0/*final*/);
/* init_transform_mech is expected to take the input depth into
* account:
*/
debug(max_depth >= png_depth);
if (max_depth < png_depth)
max_depth = png_depth;
affirm(max_depth <= (png_ptr->read_struct ? 128U : 64U));
# ifdef PNG_READ_TRANSFORMS_SUPPORTED
/* Set this now because it only gets resolved finally at this
* point.
*/
png_ptr->invalid_info = tc.invalid_info;
# endif /* READ_TRANSFORMS */
/* And check the transform fields: */
affirm(png_ptr->row_format == tc.format &&
png_ptr->row_range == tc.range &&
png_ptr->row_bit_depth == tc.bit_depth);
# ifdef PNG_READ_GAMMA_SUPPORTED
affirm(png_ptr->row_gamma == tc.gamma);
# endif /* READ_GAMMA */
png_ptr->row_max_pixel_depth =
png_check_bits(png_ptr, max_depth, 8U);
/* On 'read' input_depth is the PNG pixel depth and output_depth is
* the depth of the pixels passed to the application, but on 'write'
* the transform list is reversed so output_depth is the PNG depth
* and input_depth the application depth.
*/
{
const png_byte app_depth =
png_check_bits(png_ptr, PNG_TC_PIXEL_DEPTH(tc), 8U);
affirm(app_depth <= max_depth);
if (png_ptr->read_struct)
{
png_ptr->row_input_pixel_depth = png_depth;
png_ptr->row_output_pixel_depth = app_depth;
}
else
{
png_ptr->row_input_pixel_depth = app_depth;
png_ptr->row_output_pixel_depth = png_depth;
}
return; /* to skip the default settings below */
}
}
}
else /* png_ptr->transform_list == NULL */
{
png_ptr->row_format = png_check_bits(png_ptr,
PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type), PNG_RF_BITS);
png_ptr->row_bit_depth = png_check_bits(png_ptr, png_ptr->bit_depth,
6);
png_ptr->row_range = 0;
# ifdef PNG_READ_GAMMA_SUPPORTED
if ((png_ptr->colorspace.flags &
(PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) ==
PNG_COLORSPACE_HAVE_GAMMA)
png_ptr->row_gamma = png_ptr->colorspace.gamma;
# endif /* READ_GAMMA */
# ifdef PNG_READ_TRANSFORMS_SUPPORTED
png_ptr->invalid_info = 0U;
# endif /* READ_TRANSFORMS */
}
# endif /* TRANSFORM_MECH */
/* We get here if there are no transforms therefore no change to the pixel
* bit depths.
*/
png_ptr->row_output_pixel_depth = png_ptr->row_max_pixel_depth =
png_ptr->row_input_pixel_depth = png_depth;
}
#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
defined(PNG_WRITE_INTERLACING_SUPPORTED)
int PNGAPI
png_set_interlace_handling(png_structrp png_ptr)
{
png_debug(1, "in png_set_interlace handling");
if (png_ptr != 0)
{
if (png_ptr->read_struct)
{
# ifdef PNG_READ_INTERLACING_SUPPORTED
if (png_ptr->interlaced)
{
png_ptr->do_interlace = 1;
return PNG_INTERLACE_ADAM7_PASSES;
}
return 1;
# else /* !READ_INTERLACING */
png_app_error(png_ptr, "no de-interlace support");
/* return 0 below */
# endif /* !READ_INTERLACING */
}
else /* write */
{
# ifdef PNG_WRITE_INTERLACING_SUPPORTED
if (png_ptr->interlaced)
{
png_ptr->do_interlace = 1;
return PNG_INTERLACE_ADAM7_PASSES;
}
return 1;
# else /* !WRITE_INTERLACING */
png_app_error(png_ptr, "no interlace support");
/* return 0 below */
# endif /* !WRITE_INTERLACING */
}
}
/* API CHANGE: 1.7.0: returns 0 if called with a NULL png_ptr */
return 0;
}
#endif /* READ_INTERLACING || WRITE_INTERLACING */
#ifdef PNG_MNG_READ_FEATURES_SUPPORTED
/* Undoes intrapixel differencing, this is called immediately after the PNG
* filter has been undone.
*/
static void
png_do_read_intrapixel_RGB8(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_uint_32 width = tc->width;
tc->sp = dp;
/* TAKE CARE: dp and sp may be the same, in which case the assignments to *dp
* are overwriting sp[]
*/
do
{
*dp++ = PNG_BYTE(sp[0] + sp[1]); /* red+green */
*dp++ = *++sp; /* green */
*dp++ = PNG_BYTE(sp[0] + sp[1]); /* green+blue */
sp += 2;
}
while (--width > 0);
# define png_ptr (tc->png_ptr)
UNTESTED
# undef png_ptr
PNG_UNUSED(tr)
}
static void
png_do_read_intrapixel_RGBA8(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_uint_32 width = tc->width;
tc->sp = dp;
do
{
*dp++ = PNG_BYTE(sp[0] + sp[1]); /* red+green */
*dp++ = *++sp; /* green */
*dp++ = PNG_BYTE(sp[0] + sp[1]); /* green+blue */
sp += 2;
*dp++ = *sp++; /* alpha */
}
while (--width > 0);
# define png_ptr (tc->png_ptr)
UNTESTED
# undef png_ptr
PNG_UNUSED(tr)
}
static void
png_do_read_intrapixel_RGB16(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_uint_32 width = tc->width;
tc->sp = dp;
/* The input consists of 16-bit values and, by examination of the code
* (please, someone, check; I didn't read the spec) the differencing is done
* against the 16-bit green value.
*/
do
{
unsigned int red = png_get_uint_16(sp + 0);
unsigned int green = png_get_uint_16(sp + 2);
unsigned int blue = png_get_uint_16(sp + 4);
sp += 6;
red += green;
blue += green;
*dp++ = PNG_BYTE(red >> 8);
*dp++ = PNG_BYTE(red);
*dp++ = PNG_BYTE(green >> 8);
*dp++ = PNG_BYTE(green);
*dp++ = PNG_BYTE(blue >> 8);
*dp++ = PNG_BYTE(blue);
}
while (--width > 0);
# define png_ptr (tc->png_ptr)
UNTESTED
# undef png_ptr
PNG_UNUSED(tr)
}
static void
png_do_read_intrapixel_RGBA16(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_uint_32 width = tc->width;
tc->sp = dp;
/* As above but copy the alpha over too. */
do
{
unsigned int red = png_get_uint_16(sp + 0);
unsigned int green = png_get_uint_16(sp + 2);
unsigned int blue = png_get_uint_16(sp + 4);
sp += 6;
red += green;
blue += green;
*dp++ = PNG_BYTE(red >> 8);
*dp++ = PNG_BYTE(red);
*dp++ = PNG_BYTE(green >> 8);
*dp++ = PNG_BYTE(green);
*dp++ = PNG_BYTE(blue >> 8);
*dp++ = PNG_BYTE(blue);
*dp++ = *sp++;
*dp++ = *sp++; /* alpha */
}
while (--width > 0);
# define png_ptr (tc->png_ptr)
UNTESTED
# undef png_ptr
PNG_UNUSED(tr)
}
static void
png_init_read_intrapixel(png_transformp *tr, png_transform_controlp tc)
{
/* Double check the permitted MNG features in case the app turned the feature
* on then off again. Also make sure the color type is acceptable; it must
* be RGB or RGBA.
*/
png_const_structp png_ptr = tc->png_ptr;
if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
(png_ptr->filter_method == PNG_INTRAPIXEL_DIFFERENCING) &&
(tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) ==
PNG_FORMAT_FLAG_COLOR)
{
if (tc->init == PNG_TC_INIT_FINAL) switch (PNG_TC_PIXEL_DEPTH(*tc))
{
case 24: (*tr)->fn = png_do_read_intrapixel_RGB8; break;
case 32: (*tr)->fn = png_do_read_intrapixel_RGBA8; break;
case 48: (*tr)->fn = png_do_read_intrapixel_RGB16; break;
case 64: (*tr)->fn = png_do_read_intrapixel_RGBA16; break;
default: impossible("bit depth");
}
}
else
(*tr)->fn = NULL;
}
#endif /* MNG_READ_FEATURES_SUPPORTED */
#ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED
/* This is just the forward direction of the above:
*
* red := red - green
* blue:= blue- green
*
* Alpha is not changed.
*/
static void
png_do_write_intrapixel_RGB8(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_uint_32 width = tc->width;
tc->sp = dp;
/* TAKE CARE: dp and sp may be the same, in which case the assignments to *dp
* are overwriting sp[]
*/
do
{
*dp++ = PNG_BYTE(sp[0] - sp[1]); /* red-green */
*dp++ = *++sp; /* green */
*dp++ = PNG_BYTE(sp[0] - sp[1]); /* green-blue */
sp += 2;
}
while (--width > 0);
# define png_ptr (tc->png_ptr)
UNTESTED
# undef png_ptr
PNG_UNUSED(tr)
}
static void
png_do_write_intrapixel_RGBA8(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_uint_32 width = tc->width;
tc->sp = dp;
do
{
*dp++ = PNG_BYTE(sp[0] - sp[1]); /* red-green */
*dp++ = *++sp; /* green */
*dp++ = PNG_BYTE(sp[0] - sp[1]); /* green-blue */
sp += 2;
*dp++ = *sp++; /* alpha */
}
while (--width > 0);
# define png_ptr (tc->png_ptr)
UNTESTED
# undef png_ptr
PNG_UNUSED(tr)
}
static void
png_do_write_intrapixel_RGB16(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_uint_32 width = tc->width;
tc->sp = dp;
do
{
unsigned int red = png_get_uint_16(sp + 0);
unsigned int green = png_get_uint_16(sp + 2);
unsigned int blue = png_get_uint_16(sp + 4);
sp += 6;
red -= green;
blue -= green;
*dp++ = PNG_BYTE(red >> 8);
*dp++ = PNG_BYTE(red);
*dp++ = PNG_BYTE(green >> 8);
*dp++ = PNG_BYTE(green);
*dp++ = PNG_BYTE(blue >> 8);
*dp++ = PNG_BYTE(blue);
}
while (--width > 0);
# define png_ptr (tc->png_ptr)
UNTESTED
# undef png_ptr
PNG_UNUSED(tr)
}
static void
png_do_write_intrapixel_RGBA16(png_transformp *tr, png_transform_controlp tc)
{
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_uint_32 width = tc->width;
tc->sp = dp;
/* As above but copy the alpha over too. */
do
{
unsigned int red = png_get_uint_16(sp + 0);
unsigned int green = png_get_uint_16(sp + 2);
unsigned int blue = png_get_uint_16(sp + 4);
sp += 6;
red -= green;
blue -= green;
*dp++ = PNG_BYTE(red >> 8);
*dp++ = PNG_BYTE(red);
*dp++ = PNG_BYTE(green >> 8);
*dp++ = PNG_BYTE(green);
*dp++ = PNG_BYTE(blue >> 8);
*dp++ = PNG_BYTE(blue);
*dp++ = *sp++;
*dp++ = *sp++; /* alpha */
}
while (--width > 0);
# define png_ptr (tc->png_ptr)
UNTESTED
# undef png_ptr
PNG_UNUSED(tr)
}
static void
png_init_write_intrapixel(png_transformp *tr, png_transform_controlp tc)
{
/* Write filter_method 64 (intrapixel differencing) only if:
*
* 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED, and;
* 2. Libpng did not write a PNG signature (this filter_method is only
* used in PNG datastreams that are embedded in MNG datastreams),
* and;
* 3. The application called png_permit_mng_features with a mask that
* included PNG_FLAG_MNG_FILTER_64, and;
* 4. The filter_method is 64, and;
* 5. The color_type is RGB or RGBA
*/
png_const_structp png_ptr = tc->png_ptr;
if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
(png_ptr->filter_method == PNG_INTRAPIXEL_DIFFERENCING) &&
(tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) ==
PNG_FORMAT_FLAG_COLOR)
{
if (tc->init == PNG_TC_INIT_FINAL) switch (PNG_TC_PIXEL_DEPTH(*tc))
{
case 24: (*tr)->fn = png_do_write_intrapixel_RGB8; break;
case 32: (*tr)->fn = png_do_write_intrapixel_RGBA8; break;
case 48: (*tr)->fn = png_do_write_intrapixel_RGB16; break;
case 64: (*tr)->fn = png_do_write_intrapixel_RGBA16; break;
default: impossible("bit depth");
}
}
else
(*tr)->fn = NULL;
}
#endif /* MNG_WRITE_FEATURES */
#ifdef PNG_MNG_FEATURES_SUPPORTED
png_uint_32 PNGAPI
png_permit_mng_features(png_structrp png_ptr, png_uint_32 mng_features)
{
if (png_ptr != NULL)
{
# ifdef PNG_MNG_READ_FEATURES_SUPPORTED
if ((mng_features & PNG_FLAG_MNG_FILTER_64) != 0)
png_add_transform(png_ptr, 0/*size*/, png_init_read_intrapixel,
PNG_TR_MNG_INTRAPIXEL);
# else /* !MNG_READ_FEATURES */
if (png_ptr->read_struct)
{
png_app_error(png_ptr, "MNG not supported on read");
return;
}
# endif /* !MNG_READ_FEATURES */
# ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED
if ((mng_features & PNG_FLAG_MNG_FILTER_64) != 0)
png_add_transform(png_ptr, 0/*size*/, png_init_write_intrapixel,
PNG_TR_MNG_INTRAPIXEL);
# else /* !MNG_WRITE_FEATURES */
if (!png_ptr->read_struct)
{
png_app_error(png_ptr, "MNG not supported on write");
return;
}
# endif /* !MNG_WRITE_FEATURES */
return png_ptr->mng_features_permitted =
mng_features & PNG_ALL_MNG_FEATURES;
}
return 0;
}
#endif /* MNG_FEATURES */
#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) ||\
defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) ||\
defined(PNG_READ_SWAP_ALPHA_SUPPORTED) ||\
defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) ||\
defined(PNG_READ_FILLER_SUPPORTED) ||\
defined(PNG_WRITE_FILLER_SUPPORTED) ||\
defined(PNG_READ_STRIP_ALPHA_SUPPORTED) ||\
defined(PNG_READ_STRIP_16_TO_8_SUPPORTED) ||\
defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) ||\
defined(PNG_READ_EXPAND_16_SUPPORTED) ||\
defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
/* This is a generic transform which manipulates the bytes in an input row. The
* manipulations supported are:
*
* Channel addition (alpha or filler)
* Channel removal (alpha or filler)
* Channel swaps - RGB to BGR, alpha/filler from last to first and vice versa
*
* The output is described in blocks of output pixel size 4-bit codes encoded
* as follows:
*
* 0 Advance the source pointer by the source pixel size, start the
* code list again. This code doesn't actually exist; it is simply
* the result of emptying the code list.
* 1..3 An error (ignored; treated like 0)
* 4..7 Put filler[code-4] into the output
* 8..15 Put source byte[code-8] in the output
*
* The codes are held in a png_uint_32 parameter. transform->args is used by
* the init routine to work out the required codes. The format change is a mask
* which is XORed with the tc format. Note that the init routine works out
* whether to work from the beginning or end of the row and the codes are always
* stored LSB first in the order needed.
*/
typedef struct
{
png_transform tr;
png_uint_32 codes; /* As above */
unsigned int format; /* format after transform */
unsigned int bit_depth; /* bit depth after transform */
png_byte filler[4]; /* Filler or alpha bytes, LSB first (see below) */
} png_transform_byte_op;
static void
png_do_byte_ops_up(png_transformp *transform, png_transform_controlp tc)
/* Row width is unchanged or decreasing */
{
# define png_ptr (tc->png_ptr)
png_transform_byte_op *tr =
png_transform_cast(png_transform_byte_op, *transform);
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
const unsigned int sp_advance = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
debug(tc->bit_depth == 8 || tc->bit_depth == 16);
debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0);
tc->sp = tc->dp;
tc->format = tr->format;
tc->bit_depth = tr->bit_depth;
/* 'output' is a 32-byte buffer that is used to delay writes for 16 bytes,
* avoiding overwrite when source and destination buffers are the same.
* 'hwm' is either 32 or 16, initially '32', when the byte counter 'i'
* reaches 'hwm' the last-but-one 16 bytes are written; the bytes
* [hwm..hwm+15] modulo 32. hwm is then swapped to hwm+16 mod 32 and i
* continues to advance. i is always below hwm.
*
* At the end the whole remaining buffer from hwm to i is written.
*/
{
const png_uint_32 codes = tr->codes;
png_uint_32 code = codes;
unsigned int i, hwm; /* buffer index and high-water-mark */
png_byte output[32];
hwm = 32;
for (i=0;;)
{
unsigned int next_code = code & 0xf;
if (next_code >= 8)
output[i++] = sp[next_code-8];
else if (next_code >= 4)
output[i++] = tr->filler[next_code - 4];
else /* end code */
{
sp += sp_advance;
if (sp >= ep)
break; /* i may be == hwm at this point. */
code = codes;
continue; /* no ouput produced, skip the check */
}
code >>= 4; /* find the next code */
if (i == hwm)
{
hwm &= 0x10U; /* 0 or 16 */
memcpy(dp, output + hwm, 16);
dp += 16;
i = hwm; /* reset i if hwm was 32 */
/* hwm is only ever 16 or 32: */
hwm += 16;
}
}
/* Write from hwm to (i-1), the delay means there is always something to
* write.
*/
hwm &= 0x10U;
if (hwm == 16)
{
debug(i <= 16);
memcpy(dp, output + hwm, 16);
dp += 16;
}
if (i > 0)
memcpy(dp, output, i);
# ifndef PNG_RELEASE_BUILD
dp += i;
/* The macro expansion exceeded the limit on ANSI strings, so split it:
*/
dp -= PNG_TC_ROWBYTES(*tc);
debug(dp == tc->dp);
# endif
}
debug(sp == ep);
# undef png_ptr
}
#ifdef PNG_READ_TRANSFORMS_SUPPORTED
static void
png_do_byte_ops_down(png_transformp *transform, png_transform_controlp tc)
/* Row width is increasing */
{
# define png_ptr (tc->png_ptr)
png_transform_byte_op *tr =
png_transform_cast(png_transform_byte_op, *transform);
const png_const_bytep ep = png_voidcast(png_const_bytep, tc->sp);
const unsigned int sp_advance = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
png_const_bytep sp = ep + PNG_TC_ROWBYTES(*tc);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_alloc_size_t dest_rowbytes;
debug(tc->bit_depth == 8U || tc->bit_depth == 16U);
debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U);
tc->sp = tc->dp;
tc->format = tr->format;
tc->bit_depth = tr->bit_depth;
dest_rowbytes = PNG_TC_ROWBYTES(*tc);
dp += dest_rowbytes;
/* In this case the 32-byte buffer is written downwards with a writes delayed
* by 16 bytes as before. 'hwm' is lower than i; 0 or 16.
*/
{
const png_uint_32 codes = tr->codes;
png_uint_32 code = codes;
unsigned int size, hwm, i;
png_byte output[32] = { 0 };
/* This catches an empty codes array, which would cause all the input to
* be skipped and, potentially, a garbage output[] to be written (once) to
* *dp.
*/
affirm((codes & 0xFU) >= 4U);
/* Align the writes to a 16-byte multiple from the start of the
* destination buffer:
*/
size = dest_rowbytes & 0xFU;
if (size == 0U) size = 16U;
i = size+16U;
sp -= sp_advance; /* Move 1 pixel back */
hwm = 0U;
for (;;)
{
unsigned int next_code = code & 0xFU;
if (next_code >= 8U)
output[--i] = sp[next_code-8U];
else if (next_code >= 4U)
output[--i] = tr->filler[next_code - 4U];
else /* end code */
{
sp -= sp_advance;
if (sp < ep)
break;
code = codes;
continue; /* no ouput produced, skip the check */
}
code >>= 4; /* find the next code */
if (i == hwm)
{
/* A partial copy comes at the beginning to align the copies to a
* 16-byte boundary. The bytes to be written are the bytes
* i+16..(hwm-1) except that the partial buffer may reduce this.
*/
dp -= size;
hwm ^= 0x10U; /* == i+16 mod 32 */
memcpy(dp, output + hwm, size);
size = 16U;
if (i == 0U) i = 32U;
}
}
/* The loop above only exits with an exit code, so 'i' has been checked
* against 'hwm' before and, because of the alignment, i will always be
* either 16 or 32:
*/
debug((i == 16U || i == 32U) & (((i & 0x10U)^0x10U) == hwm));
debug(sp+sp_advance == ep);
/* At the end the bytes i..(hwm-1) need to be written, with the proviso
* that 'size' will be less than 16 for short rows. If 'size' is still a
* short value then the range to be written is output[i..16+(size-1)],
* otherwise (size == 16) either this is the first write and a full 32
* bytes will be written (hwm == 0, i == 32) or 16 bytes need to be
* written.
*/
if (size < 16U)
{
debug(i == 16U);
dp -= size;
memcpy(dp, output + i, size);
}
else /* size == 16 */
{
debug(size == 16U);
/* Write i..(hwm-1); 16 or 32 bytes, however if 32 bytes are written
* they are contiguous and i==0.
*
* hwm is 0 or 16, i is 16 or 32, swap 0 and 32:
*/
if (hwm == 0U) hwm = 32U;
if (i == 32U) i = 0U;
affirm(i < hwm);
debug(hwm == i+16U || (i == 0U && hwm == 32U));
hwm -= i;
dp -= hwm;
memcpy(dp, output+i, hwm);
}
}
debug(dp == png_upcast(png_bytep, tc->dp));
# undef png_ptr
}
#endif /* READ_TRANSFORMS */
/* 16 bit byte swapping */
static void
png_do_bswap(png_transformp *transform, png_transform_controlp tc)
{
# define png_ptr (tc->png_ptr)
png_transform_byte_op *tr =
png_transform_cast(png_transform_byte_op, *transform);
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
const png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc);
tc->format = tr->format;
tc->bit_depth = tr->bit_depth;
tc->sp = dp;
# ifdef _XOPEN_SOURCE
/* byte swapping often has incredibly fast implementations because of the
* importance in handling ethernet traffic. X/Open defines swab() for
* this purpose and it is widely supported and normally incredibly fast:
*/
debug((rowbytes & 1) == 0);
swab(sp, dp, rowbytes);
# else /* !_XOPEN_SOURCE */
{
const png_const_bytep ep = sp + rowbytes - 1;
while (sp < ep)
{
png_byte b0 = *sp++;
*dp++ = *sp++;
*dp++ = b0;
}
debug(sp == ep+1); /* even number of bytes */
}
# endif
PNG_UNUSED(transform)
# undef png_ptr
}
/* The following flags, store in tr->args, are set by the relevant PNGAPI
* png_set calls then resolved below.
*/
#define PNG_BO_STRIP_ALPHA 0x0001U /* Remove an alpha channel (read only) */
#define PNG_BO_CHOP_16_TO_8 0x0002U /* Chop 16-bit to 8-bit channels */
#define PNG_BO_GRAY_TO_RGB 0x0004U /* G <-> RGB; replicate channels */
/* QUANTIZE happens here */
#define PNG_BO_EXPAND_16 0x0008U /* Expand 8-bit channels to 16-bit */
#define PNG_BO_BGR 0x0010U /* RGB <-> BGR */
#define PNG_BO_FILLER 0x0020U /* Add a filler/alpha */
#define PNG_BO_SWAP_ALPHA 0x0040U /* xA <-> Ax; alpha swap */
#define PNG_BO_SWAP_16 0x0080U /* 16-bit channel byte swapping */
/* The following are additional flags to qualify the transforms: */
#define PNG_BO_FILLER_ALPHA 0x4000U /* The filler is an alpha value */
#define PNG_BO_FILLER_FIRST 0x8000U /* The filler comes first */
static void
png_init_byte_ops(png_transformp *transform, png_transform_controlp tc)
{
/* In the absence of png_set_quantize none of the above operations apply to a
* palette row except indirectly; they may apply if the palette was expanded,
* but this happens earlier in the pipeline.
*
* In the presence of png_set_quantize the rules are considerably more
* complex. In libpng 1.6.0 the following operations occur before
* png_do_quantize:
*
* PNG_BO_GRAY_TO_RGB (png_do_gray_to_rgb, but only sometimes)
* PNG_BO_STRIP_ALPHA (png_do_strip_channel; removes alpha)
* encode_alpha
* scale_16_to_8
* PNG_BO_CHOP_16_TO_8 (png_do_chop)
*
* The following occur afterward:
*
* PNG_BO_EXPAND_16 (png_do_expand_16)
* PNG_BO_GRAY_TO_RGB (png_do_gray_to_rgb, normally)
* PNG_BO_BGR (png_do_bgr)
* PNG_BO_FILLER (png_do_read_filler)
* PNG_BO_SWAP_ALPHA (png_do_read_swap_alpha)
* PNG_BO_SWAP_16 (png_do_swap; 16-bit byte swap)
*
* The gray to RGB operation needs to occur early for GA or gray+tRNS images
* where the pixels are being composed on a non-gray value. For the moment
* we assume that if this is necessary the following 'init' code will see RGB
* at this point.
*
* The quantize operation operates only if:
*
* 1) tc->bit_depth is 8
* 2) The color type exactly matches that required by the parameters to
* png_set_quantize; it can be RGB, RGBA or palette, but
* png_set_quantize (not the init routine) determines this.
*
* To avoid needing to know this here the two stage initialization is used
* with two transforms, one pre-quantization the other post. In the first
* stage the correct row format and depth is set up. In the second stage the
* pre-quantization transform looks for a post-quantization transform
* immediately following and, if it exists, transfers its flags to that.
*/
png_structp png_ptr = tc->png_ptr;
png_transform_byte_op *tr =
png_transform_cast(png_transform_byte_op, *transform);
png_uint_32 args = tr->tr.args;
const unsigned int png_format = tc->format;
unsigned int format = png_format; /* memory format */
const unsigned int png_bit_depth = tc->bit_depth;
unsigned int bit_depth = png_bit_depth; /* memory bit depth */
debug(tc->init);
/* Channel swaps do not occur on COLORMAP format data at present because the
* COLORMAP is limited to 1 byte per pixel (so there is nothing to
* manipulate). Likewise for low bit depth gray, however the code below may
* widen 8-bit gray to RGB.
*/
if ((png_format & PNG_FORMAT_FLAG_COLORMAP) != 0U || png_bit_depth < 8U)
{
tr->tr.fn = NULL;
return;
}
/* This will normally happen in TC_INIT_FORMAT, but if there is a
* png_do_quantize operation which doesn't apply (this is unlikely) it will
* happen in TC_INIT_FINAL.
*/
if (tr->tr.next != NULL && tr->tr.next->order == PNG_TR_CHANNEL_POSTQ)
{
debug(tr->tr.order == PNG_TR_CHANNEL_PREQ);
/* So we can merge this transform into the next one, note that because the
* PNG_BO_FILLER operation is POSTQ we don't need to copy anything other
* than the flags.
*/
debug((args & tr->tr.next->args) == 0U);
tr->tr.next->args |= args;
tr->tr.fn = NULL;
return;
}
/* Else compact the flags for this transform - this is done in both
* TC_INIT_FORMAT and TC_INIT_FINAL because it is safer that way; the copy
* above shouldn't actually affect the results but might result in TO8 and
* TO16 cancelling each other because they are in separate transforms before
* the merge above.
*
* QUIET API CHANGE:
* For compatiblity with earlier versions of libpng these tests need to
* occur in the same order as the earlier transforms; 'TO8' combined with
* 'TO16' did actually do something to 16-bit data, however now it just
* preserves the original bit depth.
*/
if ((args & PNG_BO_STRIP_ALPHA) != 0U)
{
if ((format & PNG_FORMAT_FLAG_ALPHA) != 0U)
format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
else
args &= PNG_BIC_MASK(PNG_BO_STRIP_ALPHA);
}
if ((args & PNG_BO_CHOP_16_TO_8) != 0U)
{
/* This is the quiet API CHANGE; in fact it isn't necessary, but it
* seems likely that requesting both operations is a mistake:
*/
if ((args & PNG_BO_EXPAND_16) != 0U)
args &= PNG_BIC_MASK(PNG_BO_CHOP_16_TO_8|PNG_BO_EXPAND_16);
else if (bit_depth == 16U)
{
bit_depth = 8U;
/* This also makes the tRNS chunk unusable: */
tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL;
/* These need further processing: PNG_INFO_sBIT, PNG_INFO_bKGD */
}
else
args &= PNG_BIC_MASK(PNG_BO_CHOP_16_TO_8);
}
/* QUANTIZE happens here */
if ((args & PNG_BO_EXPAND_16) != 0U)
{
/* This only does the 8 to 16-bit part of the expansion by multiply by
* 65535/255 (257) using byte replication. The cases of low bit depth
* gray being expanded to 16-bit have to be handled separately.
*/
if (bit_depth == 8U)
bit_depth = 16U;
else
args &= PNG_BIC_MASK(PNG_BO_EXPAND_16);
}
if ((args & PNG_BO_GRAY_TO_RGB) != 0U)
{
if ((format & PNG_FORMAT_FLAG_COLOR) == 0U)
format |= PNG_FORMAT_FLAG_COLOR;
else
args &= PNG_BIC_MASK(PNG_BO_GRAY_TO_RGB);
}
if ((args & PNG_BO_BGR) != 0U)
{
/* This does not happen on colormaps: */
if ((format & PNG_FORMAT_FLAG_COLOR) != 0U && !tc->palette)
format |= PNG_FORMAT_FLAG_BGR;
else
args &= PNG_BIC_MASK(PNG_BO_BGR);
}
if ((args & PNG_BO_FILLER) != 0U)
{
if ((format & PNG_FORMAT_FLAG_ALPHA) == 0U)
{
format |= PNG_FORMAT_FLAG_ALPHA;
tc->channel_add = 1U;
/* And SWAP_ALPHA did not occur, because prior to 1.7.0 the filler op
* did not set ALPHA in the color type, so use SWAP_ALPHA to handle the
* before/after filler location.
*
* NOTE: this occurs twice, once in TC_START and once in TC_FINAL, but
* that is ok, the operations are idempotent.
*
* For colormaps (tc->palette set) the filler will just end up setting
* all the tRNS entries and PNG_BO_SWAP_ALPHA will be cancelled below.
*/
if ((args & PNG_BO_FILLER_FIRST) != 0U)
args |= PNG_BO_SWAP_ALPHA;
else
args &= PNG_BIC_MASK(PNG_BO_SWAP_ALPHA);
if (!(args & PNG_BO_FILLER_ALPHA)) /* filler is not alpha */
format |= PNG_FORMAT_FLAG_AFILLER;
}
else
args &= PNG_BIC_MASK(PNG_BO_FILLER);
}
if ((args & PNG_BO_SWAP_ALPHA) != 0U)
{
/* This does not happen on color maps: */
if ((format & PNG_FORMAT_FLAG_ALPHA) != 0U && !tc->palette)
format |= PNG_FORMAT_FLAG_AFIRST;
else
args &= PNG_BIC_MASK(PNG_BO_SWAP_ALPHA);
}
if ((args & PNG_BO_SWAP_16) != 0U)
{
if (bit_depth == 16U)
format |= PNG_FORMAT_FLAG_SWAPPED;
else
args &= PNG_BIC_MASK(PNG_BO_SWAP_16);
}
if (args != 0U)
{
/* At the end (TC_INIT_FINAL) work out the mapping array using the codes
* defined above and store the format and bit depth changes (as changes,
* so they will work either forward or backward). The filler array must
* be set up by the png_set API.
*/
if (tc->init == PNG_TC_INIT_FINAL)
{
const unsigned int png_pixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
tc->format = format;
tc->bit_depth = bit_depth;
{
const unsigned int memory_pixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
unsigned int code_size, src_size;
int go_down;
png_byte codes[8];
/* The codes array maps the PNG format into the memory format
* assuming the mapping works upwards in the address space.
* Initially ignore the bit depth and just work on the first four
* bytes.
*/
codes[0] = 8U;
codes[1] = 9U;
codes[2] = 10U;
codes[3] = 11U;
codes[7] = codes[6] = codes[5] = codes[4] = 0U/*error*/;
/* PNG_BO_STRIP_ALPHA: handled by memory_pixel_size */
/* PNG_BO_CHOP_16_TO_8: handled below */
/* PNG_BO_EXPAND_16: handled below */
if ((args & PNG_BO_GRAY_TO_RGB) != 0U)
{
codes[3] = 9U; /* alpha, if present */
codes[2] = codes[1] = 8U;
# ifdef PNG_READ_tRNS_SUPPORTED
/* Gray to RGB, so copy the tRNS G value into r,g,b: */
if (png_ptr->num_trans == 1U)
png_ptr->trans_color.blue =
png_ptr->trans_color.green =
png_ptr->trans_color.red =
png_ptr->trans_color.gray;
# endif /* READ_tRNS */
}
/* 'BGR' and gray-to-RGB are mutually exclusive; with gray-to-RGB
* codes[0] == codes[2] == 8
*/
else if ((args & PNG_BO_BGR) != 0U)
{
codes[0] = 10U;
codes[2] = 8U;
}
if ((args & PNG_BO_FILLER) != 0U)
{
/* The filler alway goes after; for a 'before' filler the code
* above turns on SWAP_ALPHA too. The gray-to-RGB transform has
* happened already, so the location of the filler channel is
* given by 'format':
*/
if ((format & PNG_FORMAT_FLAG_COLOR) != 0U)
codes[3] = 4U; /* low byte of filler */
else
codes[1] = 4U;
}
if ((args & PNG_BO_SWAP_ALPHA) != 0U)
{
if ((format & PNG_FORMAT_FLAG_COLOR) != 0U)
{
/* BGR may have swapped the early codes. gray-to-RGB may have
* set them all to '8':
*/
png_byte acode = codes[3];
codes[3] = codes[2];
codes[2] = codes[1];
codes[1] = codes[0];
codes[0] = acode;
}
else /* GA format */
codes[0] = codes[1], codes[1] = 8U;
}
/* PNG_BO_SWAP_16: 16-bit only, handled below */
/* Now the 16-bit dependent stuff. */
if ((args & PNG_BO_CHOP_16_TO_8) != 0U)
{
/* 16-bit input, 8-bit output, happens before FILLER so the
* filler must be an 8-bit value. Apart from a filler code (4 in
* this case) the code must be adjusted from byte 'x' to byte
* '2x' to select the MSB of each 16-bit channel.
*
* We must use PNG_FORMAT_CHANNELS here because the memory pixel
* size might (in the future) include a TO16 operation.
*/
unsigned int i = PNG_FORMAT_CHANNELS(format);
while (i > 0U)
{
unsigned int code = codes[--i];
if (code > 8U) /* 8, 4 need not change */
codes[i] = PNG_BYTE(8U+2U*(code-8U));
}
}
if ((args & PNG_BO_EXPAND_16) != 0U)
{
/* Don't expect this with CHOP, but it will work, setting the low
* 8-bits of each 16-bit value to the high bits.
*/
unsigned int i = PNG_FORMAT_CHANNELS(format);
while (i > 0U)
{
png_byte code = codes[--i];
/* BSWAP is after FILLER, however the data passed in is a
* machine native png_uint_16. We don't know until this init
* routine whether the data is an 8 or 16-bit value because we
* don't know the full set of transforms the app will apply
* when the png_set_filler API is called.
*
* This means that the data in tr->filler[] needs to have the
* low bits in a known place, so the code here puts the low 8
* bits in filler[0], code 4. Hence the following:
*/
if (code == 4U)
codes[2U*i/*MSB*/] = 5U, codes[2U*i+1U/*LSB*/] = 4U;
else
codes[2U*i] = codes[2U*i+1U] = code;
}
# ifdef PNG_READ_tRNS_SUPPORTED
/* We're just duplicating bytes, so the tRNS chunk can be
* maintained if present. If the tRNS is for a colormap this
* produces garbage in trans_color, but it isn't used.
*/
if (png_ptr->num_trans == 1U)
{
# define TO16(x) x = PNG_UINT_16((x & 0xFFU) * 0x101U)
TO16(png_ptr->trans_color.gray);
TO16(png_ptr->trans_color.red);
TO16(png_ptr->trans_color.green);
TO16(png_ptr->trans_color.blue);
# undef TO16
}
# endif /* READ_tRNS */
}
else if (bit_depth == 16U)
{
/* 16-bit input and output. */
unsigned int i = PNG_FORMAT_CHANNELS(format);
while (i > 0U)
{
unsigned int code = codes[--i];
if (code == 4U) /* as above */
codes[2U*i/*MSB*/] = 5U, codes[2U*i+1U/*LSB*/] = 4U;
else
{
codes[2U*i] = PNG_BYTE(8U+2U*(code-8U));
codes[2U*i+1U] = PNG_BYTE(8U+2U*(code-8U)+1U);
}
}
}
if ((args & PNG_BO_SWAP_16) != 0U)
{
/* bswap the memory bytes. */
unsigned int i;
png_byte bswap_codes[sizeof codes];
debug((memory_pixel_size & 1U) == 0U);
for (i=0U; i<sizeof codes; ++i)
bswap_codes[i] = codes[i ^ 1U];
memcpy(codes, bswap_codes, sizeof codes);
}
# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
/* Handle the 'write' case; the codes[] array must be inverted,
* it lists the PNG pixel for each memory pixel, we need it to
* list the memory pixel for each PNG pixel.
*/
if (!png_ptr->read_struct)
{
/* There are no write transforms that add data to the PNG
* file; the 'filler' transform removes a channel, but that is
* the limit of the changes.
*/
unsigned int i = 0U;
png_byte write_codes[8U];
memset(write_codes, 0, sizeof write_codes);
while (i<memory_pixel_size)
{
unsigned int code = codes[i];
if (code >= 8U) /* 8+index of PNG byte */
write_codes[code-8U] = PNG_BYTE(8U+i);
/* else this is a filler byte to be removed */
else
debug(code == 4U || code == 5U);
++i;
}
code_size = png_pixel_size;
src_size = memory_pixel_size;
tr->format = png_format;
tr->bit_depth = png_bit_depth;
/* The PNG size should always be <= to the memory size, the
* source pointer will be the memory, the destination the PNG
* format, so it should always be possible to do the upwards
* copy.
*/
go_down = png_pixel_size > memory_pixel_size;
affirm(!go_down);
memcpy(codes, write_codes, sizeof codes);
}
else
# endif /* WRITE_TRANSFORMS */
{
code_size = memory_pixel_size;
src_size = png_pixel_size;
tr->format = format;
tr->bit_depth = bit_depth;
go_down = png_pixel_size < memory_pixel_size;
}
/* Record this for debugging: */
tr->tr.args = args;
/* For the same-pixel-size case check for a bswap; this is available
* in heavily optimized forms and is a common operation (50% of the
* time) with 16-bit PNG data, particularly given the handling in
* the simplified API.
*/
if (!go_down)
{
if (memory_pixel_size == png_pixel_size)
{
int the_same = 1;
int swapped = (memory_pixel_size & 1) == 0; /* even count */
unsigned int i;
for (i=0U; i<memory_pixel_size; ++i)
{
if (codes[i] != 8U+i)
{
the_same = 0;
if (codes[i] != 8U+(i^1U))
swapped = 0;
if (!swapped)
break;
}
else /* byte is copied, so it can't be swapped! */
{
swapped = 0;
if (!the_same)
break;
}
}
/* Use the 'bswap' routine if possible. */
if (swapped)
{
tr->tr.fn = png_do_bswap;
return;
}
else if (the_same)
impossible("not reached");
}
tr->tr.fn = png_do_byte_ops_up;
/* Construct the code, forwards: */
{
unsigned int i = code_size;
png_uint_32 code = 0U;
while (i > 0U)
{
unsigned int next = codes[--i];
code <<= 4U;
if ((next >= 8U && next < 8U+src_size) ||
next == 4U || next == 5U)
code += next;
else
impossible("invalid code (up)");
}
tr->codes = code;
}
}
else /* go_down */
# ifdef PNG_READ_TRANSFORMS_SUPPORTED
{
tr->tr.fn = png_do_byte_ops_down;
/* Construct the code, backwards: */
{
unsigned int i = 0U;
png_uint_32 code = 0U;
while (i < code_size)
{
unsigned int next = codes[i++];
code <<= 4;
if ((next >= 8U && next < 8U+src_size) ||
next == 4U || next == 5U)
code += next;
else
impossible("invalid code (down)");
}
tr->codes = code;
}
}
# else /* !READ_TRANSFORMS */
impossible("not reached"); /* because of the affirm above */
# endif /* !READ_TRANSFORMS */
}
}
else /* TC_INIT_FORMAT: just store modified 'args' */
{
tc->format = format;
tc->bit_depth = bit_depth;
tr->tr.args = args;
}
}
else /* the transform is not applicable */
tr->tr.fn = NULL;
}
#endif /* SWAP poo */
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
static void
png_init_rgb_to_gray_byte_ops(png_transformp *transform,
png_transform_controlp tc)
{
/* This just delay initializes the function; all the transform initialization
* has been done below.
*/
(*transform)->fn = png_do_byte_ops_up;
/* If this happens on a row do the transform immediately: */
if (!tc->init)
png_do_byte_ops_up(transform, tc);
else
{
/* This doing the init - update the row information here */
# define png_ptr (tc->png_ptr)
png_transform_byte_op *tr =
png_transform_cast(png_transform_byte_op, *transform);
debug(tc->bit_depth == 8U || tc->bit_depth == 16U);
debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U &&
(tc->format & PNG_FORMAT_FLAG_COLOR) != 0U);
tc->format = tr->format;
tc->bit_depth = tr->bit_depth;
# undef png_ptr
}
}
void /* PRIVATE */
png_add_rgb_to_gray_byte_ops(png_structrp png_ptr, png_transform_controlp tc,
unsigned int index, unsigned int order)
/* Add a byte_ops transform to convert RGB or RGBA data to 'gray' by
* selecting just the given change [index] The transform added is added at
* 'order'.
*/
{
png_transform_byte_op *tr = png_transform_cast(png_transform_byte_op,
png_add_transform(png_ptr, sizeof (png_transform_byte_op),
png_init_rgb_to_gray_byte_ops, order));
affirm((tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) ==
PNG_FORMAT_FLAG_COLOR &&
index <= 2 && tc->init == PNG_TC_INIT_FINAL);
tr->format = tc->format & PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
tr->bit_depth = tc->bit_depth;
/* For 1 byte channel [index] plus, maybe, alpha: */
if (tc->bit_depth == 8)
tr->codes = 8U + index +
((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ? (8U+3U) << 4 : 0U);
else
{
affirm(tc->bit_depth == 16);
/* As above, but two bytes; [2*index] and [2*index+1] */
index *= 2U;
tr->codes = (8U + index) + ((9U + index) << 4) +
((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ?
((8U+6U) + ((9U+6U) << 4)) << 8 : 0U);
}
}
#endif /* READ_RGB_TO_GRAY */
#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) &&\
defined(PNG_READ_BACKGROUND_SUPPORTED)
void /* PRIVATE */
png_push_gray_to_rgb_byte_ops(png_transformp *transform,
png_transform_controlp tc)
/* This is an init-time utility to add appropriate byte ops to expand a
* grayscale PNG data set to RGB.
*/
{
# define png_ptr (tc->png_ptr)
png_transformp tr = png_push_transform(png_ptr,
sizeof (png_transform_byte_op), png_init_byte_ops, transform, NULL);
tr->args = PNG_BO_GRAY_TO_RGB;
debug(tr == *transform);
png_init_byte_ops(transform, tc);
# undef png_ptr
}
#endif /* GRAY_TO_RGB && READ_BACKGROUND */
#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
void /* PRIVATE */
png_add_strip_alpha_byte_ops(png_structrp png_ptr)
{
png_add_transform(png_ptr, sizeof (png_transform_byte_op), png_init_byte_ops,
PNG_TR_CHANNEL_PREQ)->args |= PNG_BO_STRIP_ALPHA;
}
#endif /* READ_STRIP_ALPHA */
#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
/* Chop 16-bit depth files to 8-bit depth */
void PNGAPI
png_set_strip_16(png_structrp png_ptr)
{
if (png_ptr != NULL)
png_add_transform(png_ptr, sizeof (png_transform_byte_op),
png_init_byte_ops, PNG_TR_CHANNEL_PREQ)->args |=
PNG_BO_CHOP_16_TO_8;
}
#endif /* READ_STRIP_16_TO_8 */
#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
void PNGAPI
png_set_gray_to_rgb(png_structrp png_ptr)
{
if (png_ptr != NULL)
{
png_set_expand_gray_1_2_4_to_8(png_ptr);
png_add_transform(png_ptr, sizeof (png_transform_byte_op),
png_init_byte_ops, PNG_TR_CHANNEL_PREQ)->args |=
PNG_BO_GRAY_TO_RGB;
}
}
#endif /* READ_GRAY_TO_RGB */
/* QUANTIZE */
#ifdef PNG_READ_EXPAND_16_SUPPORTED
/* Expand to 16-bit channels. PNG_BO_EXPAND_16 also expands the tRNS chunk if
* it is present, but it requires low bit depth grayscale expanded first. This
* must also force palette to RGB.
*/
void PNGAPI
png_set_expand_16(png_structrp png_ptr)
{
if (png_ptr != NULL)
{
png_set_expand_gray_1_2_4_to_8(png_ptr);
png_set_palette_to_rgb(png_ptr);
png_add_transform(png_ptr, sizeof (png_transform_byte_op),
png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
PNG_BO_EXPAND_16;
}
}
#endif /* READ_EXPAND_16 */
#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
void PNGAPI
png_set_bgr(png_structrp png_ptr)
{
if (png_ptr != NULL)
{
# ifndef PNG_READ_BGR_SUPPORTED
if (png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_bgr not supported on read");
return;
}
# endif
# ifndef PNG_WRITE_BGR_SUPPORTED
if (!png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_bgr not supported on write");
return;
}
# endif
png_add_transform(png_ptr, sizeof (png_transform_byte_op),
png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
PNG_BO_BGR;
}
}
#endif /* BGR */
#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
/* This includes png_set_filler and png_set_add_alpha. The only difference
* between the two is that the latter resulted in PNG_COLOR_MASK_ALPHA being
* added to the info_ptr color type, if png_read_update_info was called whereas
* the former did not.
*
* Regardless of whether the added channel resulted in the change to the
* png_info color type, the SWAP_ALPHA transform was not performed, even though
* it apparently occured after the add, because PNG_COLOR_MASK_ALPHA was never
* set in the 1.6 'row_info'.
*
* Consequently 'SWAP_ALPHA' and 'FILLER' were independent; one or the other
* would occur depending on the color type (not the number of channels) prior to
* the two transforms.
*
* Prior to 1.7.0 the app could obtain information about the memory format by
* calling png_read_update_info followed by png_get_color_type and
* png_get_channels. The first would return PNG_COLOR_TYPE..._ALPHA if
* png_set_add_alpha was performed and the base type if png_set_filler was
* performed, however in both cases png_get_channels would return the extra
* channel; 2 or 4.
*
* The app could also insert a user transform callback and view the color type
* in the old "row_info" structure, however this resulted in an inconsistent
* color type because png_set_alpha did not add COLOR_MASK_ALPHA to the color
* type.
*
* Hence API CHANGE: 1.7.0, row transform callbacks now see the same color type
* as reported by png_get_color_type after png_read_update_info.
*/
/* Add a filler byte on read, or remove a filler or alpha byte on write.
* The filler type has changed in v0.95 to allow future 2-byte fillers
* for 48-bit input data, as well as to avoid problems with some compilers
* that don't like bytes as parameters.
*/
static void
set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc, int alpha)
{
if (png_ptr != NULL)
{
if (filler_loc != PNG_FILLER_BEFORE && filler_loc != PNG_FILLER_AFTER)
{
png_app_error(png_ptr, "png_set_filler: invalid filler location");
return;
}
# ifndef PNG_READ_SWAP_SUPPORTED
if (png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_filler not supported on read");
return;
}
# endif
# ifndef PNG_WRITE_SWAP_SUPPORTED
if (!png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_filler not supported on write");
return;
}
# endif
{
png_transform_byte_op *tr =
png_transform_cast(png_transform_byte_op,
png_add_transform(png_ptr, sizeof (png_transform_byte_op),
png_init_byte_ops, PNG_TR_CHANNEL_POSTQ));
png_uint_32 args = PNG_BO_FILLER;
if (filler_loc == PNG_FILLER_BEFORE)
args |= PNG_BO_FILLER_FIRST;
if (alpha)
args |= PNG_BO_FILLER_ALPHA;
tr->tr.args |= args;
/* The filler must be stored LSByte first: */
tr->filler[0] = PNG_BYTE(filler >> 0);
tr->filler[1] = PNG_BYTE(filler >> 8);
tr->filler[2] = PNG_BYTE(filler >> 16);
tr->filler[3] = PNG_BYTE(filler >> 24);
}
}
}
void PNGAPI
png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
{
set_filler(png_ptr, filler, filler_loc, 0/*!alpha*/);
}
/* Added to libpng-1.2.7 */
void PNGAPI
png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
{
set_filler(png_ptr, filler, filler_loc, 1/*alpha*/);
}
#endif /* FILLER */
#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
void PNGAPI
png_set_swap_alpha(png_structrp png_ptr)
{
if (png_ptr != NULL)
{
# ifndef PNG_READ_SWAP_SUPPORTED
if (png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_swap_alpha not supported on read");
return;
}
# endif
# ifndef PNG_WRITE_SWAP_SUPPORTED
if (!png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_swap_alpha not supported on write");
return;
}
# endif
png_add_transform(png_ptr, sizeof (png_transform_byte_op),
png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
PNG_BO_SWAP_ALPHA;
}
}
#endif /* SWAP_ALPHA */
#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
void PNGAPI
png_set_swap(png_structrp png_ptr)
{
if (png_ptr != NULL)
{
# ifndef PNG_READ_SWAP_SUPPORTED
if (png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_swap not supported on read");
return;
}
# endif
# ifndef PNG_WRITE_SWAP_SUPPORTED
if (!png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_swap not supported on write");
return;
}
# endif
png_add_transform(png_ptr, sizeof (png_transform_byte_op),
png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
PNG_BO_SWAP_16;
}
}
#endif /* SWAP */
#if defined(PNG_READ_PACKSWAP_SUPPORTED) ||\
defined(PNG_WRITE_PACKSWAP_SUPPORTED) ||\
defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\
defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) ||\
defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
static png_alloc_size_t
row_align(png_transform_controlp tc)
/* Utiltity to align the source row (sp) in a transform control; it does this
* by simply copying it to dp if it is not already aligned. As a convenience
* the utility returns the number of bytes in the row.
*/
{
png_const_structp png_ptr = tc->png_ptr;
png_const_voidp sp = tc->sp;
png_voidp dp = tc->dp;
png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc);
/* For alignment; if png_alignof is not supported by the compiler this will
* always do an initial memcpy if the source and destination are not the
* same. We can only get here for write; the read case always uses locally
* allocated buffers, only write reads from the application data directly.
*/
# ifdef png_alignof
debug(png_isaligned(dp, png_uint_32));
# endif
if (sp != dp && !png_ptr->read_struct && !png_isaligned(sp, png_uint_32))
{
UNTESTED
memcpy(dp, sp, rowbytes);
tc->sp = dp;
}
return rowbytes;
}
#endif /* Stuff that needs row_align */
#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\
defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) ||\
defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
/* Bit-ops; invert bytes. This works for mono inverts too because even the low
* bit depths can be handled as bytes (since there can be no intervening
* channels).
*/
#define PNG_B_INVERT_MONO 1U
#define PNG_B_INVERT_RGB 2U /* not set, used below */
#define PNG_B_INVERT_ALPHA 4U
typedef struct
{
png_transform tr;
unsigned int step0; /* initial advance on sp and dp */
unsigned int step; /* advance after start */
png_uint_32 mask; /* XOR mask */
} png_transform_bit_op;
static void
png_do_invert_all(png_transformp *transform, png_transform_controlp tc)
{
const png_const_structp png_ptr = tc->png_ptr;
/* Invert the whole row, quickly */
const png_const_voidp dp_end = png_upcast(png_bytep, tc->dp) + row_align(tc);
png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
tc->sp = dp;
if (png_ptr->read_struct)
{
tc->format |= PNG_FORMAT_FLAG_RANGE;
tc->range++;
}
else if (--(tc->range) == 0)
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);
while (png_upcast(void*,dp) < dp_end)
*dp++ = ~*sp++;
PNG_UNUSED(transform)
}
static void
png_do_invert_channel(png_transformp *transform, png_transform_controlp tc)
{
const png_const_structp png_ptr = tc->png_ptr;
/* Invert just one channel in the row. */
const png_transform_bit_op * const tr =
png_transform_cast(png_transform_bit_op, *transform);
const png_uint_32 mask = tr->mask;
const unsigned int step = tr->step;
const unsigned int step0 = tr->step0;
const png_const_voidp dp_end = png_upcast(png_bytep, tc->dp) + row_align(tc);
png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
tc->sp = dp;
if (png_ptr->read_struct)
{
tc->format |= PNG_FORMAT_FLAG_RANGE;
tc->range++;
}
else if (--(tc->range) == 0)
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);
if (sp == dp || step == 1)
{
sp += step0;
dp += step0;
while (png_upcast(void*,dp) < dp_end)
*dp = *sp ^ mask, dp += step, sp += step;
}
else /* step == 2, copy required */
{
if (step0) /* must be 1 */
*dp++ = *sp++;
while (png_upcast(void*,dp) < dp_end)
{
*dp++ = *sp++ ^ mask;
if (!(png_upcast(void*,dp) < dp_end))
break;
*dp++ = *sp++;
}
}
PNG_UNUSED(transform)
}
static void
png_init_invert(png_transformp *transform, png_transform_controlp tc)
{
# define png_ptr (tc->png_ptr)
png_transform_bit_op *tr =
png_transform_cast(png_transform_bit_op, *transform);
png_uint_32 invert = tr->tr.args;
png_uint_32 present; /* channels present */
if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
present = 0;
else /* not color mapped */
{
if ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0)
present = PNG_B_INVERT_RGB;
else
present = PNG_B_INVERT_MONO;
if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0)
present |= PNG_B_INVERT_ALPHA;
}
/* Cannot invert things that aren't there: */
invert &= present;
/* If nothing can be inverted is present the transform is not applicable: */
if (invert == 0)
(*transform)->fn = NULL;
else
{
tc->format |= PNG_FORMAT_FLAG_RANGE;
tc->range++;
if (tc->init == PNG_TC_INIT_FINAL)
{
/* If everything that is present is to be inverted just invert the
* whole row:
*/
if (invert == present)
(*transform)->fn = png_do_invert_all;
else
{
/* One thing is to be inverted, G or A: */
unsigned int channels = PNG_TC_CHANNELS(*tc);
unsigned int channel =
(tc->format & PNG_FORMAT_FLAG_AFIRST) != 0 ? 0 : channels-1;
affirm(channels == 2 || channels == 4);
if (invert != PNG_B_INVERT_ALPHA)
{
debug(invert == PNG_B_INVERT_MONO && channels == 2 &&
present == PNG_B_INVERT_MONO+PNG_B_INVERT_ALPHA);
channel = (channels-1) - channel;
}
affirm(tc->bit_depth == 8 || tc->bit_depth == 16);
/* So channels[channel] is to be inverted, make a mask: */
{
union
{
png_byte bytes[8];
png_uint_32 words[2];
} masks;
memset(&masks, 0, sizeof masks);
if (tc->bit_depth == 8)
{
/* channels is 2 or 4, channel < 4. */
masks.bytes[channel+channels] = masks.bytes[channel] = 0xff;
tr->step = 1;
tr->mask = masks.words[0];
tr->step0 = 0;
}
else /* tc->bit_depth == 16 */
{
channel <<= 1; /* in bytes */
masks.bytes[channel+1] = masks.bytes[channel] = 0xff;
if (channels == 2)
{
tr->step = 1;
tr->mask = masks.words[0];
tr->step0 = 0;
}
else /* channels == 4 */
{
tr->step = 2;
if (masks.words[0] == 0)
{
tr->mask = masks.words[1];
tr->step0 = 1;
}
else
{
tr->mask = masks.words[0];
tr->step0 = 0;
}
}
}
}
(*transform)->fn = png_do_invert_channel;
}
}
}
# undef png_ptr
}
#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\
defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
void PNGAPI
png_set_invert_alpha(png_structrp png_ptr)
{
if (png_ptr != NULL)
{
png_add_transform(png_ptr, sizeof (png_transform_bit_op),
png_init_invert, PNG_TR_INVERT)->args |= PNG_B_INVERT_ALPHA;
# ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
/* This is necessary to avoid palette processing on write; the only
* transform that applies to colormapped images is the tRNS chunk
* invert.
*/
png_ptr->write_invert_alpha = 1U;
# endif
}
}
#endif /* INVERT_ALPHA */
#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
void PNGAPI
png_set_invert_mono(png_structrp png_ptr)
{
if (png_ptr != NULL)
png_add_transform(png_ptr, sizeof (png_transform_bit_op),
png_init_invert, PNG_TR_INVERT)->args |= PNG_B_INVERT_MONO;
}
#endif /* INVERT */
#endif /* INVERT_ALPHA || INVERT */
/*
* WARNING
* WARNING
* WARNING
* WARNING
* WARNING The transforms below are temporary; they can and will be
* WARNING heavily optimized before release.
* WARNING
* WARNING
* WARNING
*/
#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
typedef struct
{
png_transform tr;
png_color_8 true_bits;
} png_transform_shift;
/* Shift pixel values to take advantage of whole range. Pass the
* true number of bits in bit_depth. The row should be packed
* according to tc->bit_depth. Thus, if you had a row of
* bit depth 4, but the pixels only had values from 0 to 7, you
* would pass 3 as bit_depth, and this routine would translate the
* data to 0 to 15.
*
* NOTE: this is horrible complexity for no value. Once people suggested they
* were selling 16-bit displays with 5:6:5 bits spread R:G:B but so far as I
* could determine these displays produced intermediate grey (uncolored) colors,
* which is impossible with a true 5:6:5, so most likely 5:6:5 was marketing.
*/
static unsigned int
set_shifts(unsigned int format, unsigned int bit_depth,
png_const_color_8p true_bits, int *shift_start, int *shift_dec)
{
unsigned int channels = 0;
if ((format & (PNG_FORMAT_FLAG_ALPHA+PNG_FORMAT_FLAG_AFIRST)) ==
(PNG_FORMAT_FLAG_ALPHA+PNG_FORMAT_FLAG_AFIRST))
++channels; /* filled in below */
if ((format & PNG_FORMAT_FLAG_COLOR) != 0)
{
unsigned int offset = /* 0 or 2 as appropriate for red */
((format & PNG_FORMAT_FLAG_BGR) != 0) << 1;
shift_start[channels+offset] = bit_depth - true_bits->red;
if (shift_dec != NULL) shift_dec[channels+offset] = true_bits->red;
shift_start[channels+1] = bit_depth - true_bits->green;
if (shift_dec != NULL) shift_dec[channels+1] = true_bits->green;
offset ^= 2; /* for blue */
shift_start[channels+offset] = bit_depth - true_bits->blue;
if (shift_dec != NULL) shift_dec[channels+offset] = true_bits->blue;
channels += 3;
}
else /* no color: gray */
{
shift_start[channels] = bit_depth - true_bits->gray;
if (shift_dec != NULL) shift_dec[channels] = true_bits->gray;
++channels;
}
if ((format & PNG_FORMAT_FLAG_ALPHA) != 0)
{
const unsigned int offset =
(format & PNG_FORMAT_FLAG_AFIRST) != 0 ? 0 : channels++;
shift_start[offset] = bit_depth - true_bits->alpha;
if (shift_dec != NULL) shift_dec[offset] = true_bits->alpha;
}
return channels;
}
#ifdef PNG_WRITE_SHIFT_SUPPORTED
static void
png_do_shift(png_transformp *transform, png_transform_controlp tc)
{
# define png_ptr (tc->png_ptr)
png_transform_shift *tr =
png_transform_cast(png_transform_shift, *transform);
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_const_bytep dp_end = dp + PNG_TC_ROWBYTES(*tc);
png_debug(1, "in png_do_shift");
if (--(tc->range) == 0)
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);
tc->sp = dp;
{
int shift_start[4], shift_dec[4];
unsigned int channels = set_shifts(tc->format, tc->bit_depth,
&tr->true_bits, shift_start, shift_dec);
debug(PNG_TC_CHANNELS(*tc) == channels);
/* With low res depths, could only be grayscale, so one channel */
if (tc->bit_depth < 8)
{
unsigned int mask;
UNTESTED
affirm(channels == 1);
/* This doesn't matter but we expect to run before packswap: */
debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED));
if (tr->true_bits.gray == 1 && tc->bit_depth == 2)
mask = 0x55;
else if (tc->bit_depth == 4 && tr->true_bits.gray == 3)
mask = 0x11;
else
mask = 0xff;
while (dp < dp_end)
{
int j;
unsigned int v, out;
v = *sp++;
out = 0;
for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
{
if (j > 0)
out |= v << j;
else
out |= (v >> (-j)) & mask;
}
*dp++ = png_check_byte(png_ptr, out);
}
}
else if (tc->bit_depth == 8)
{
unsigned int c = 0;
UNTESTED
while (dp < dp_end)
{
int j;
unsigned int v, out;
v = *sp++;
out = 0;
for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
{
if (j > 0)
out |= v << j;
else
out |= v >> (-j);
}
*dp++ = png_check_byte(png_ptr, out);
if (++c == channels) c = 0;
}
}
else /* tc->bit_depth == 16 */
{
unsigned int c = 0, s0, s1;
UNTESTED
if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0)
s0 = 0, s1 = 8; /* LSB */
else
s0 = 8, s1 = 0; /* MSB */
while (dp < dp_end)
{
int j;
unsigned int value, v;
v = *sp++ << s0;
v += *sp++ << s1;
value = 0;
for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
{
if (j > 0)
value |= v << j;
else
value |= v >> (-j);
}
*dp++ = PNG_BYTE(value >> s0);
*dp++ = PNG_BYTE(value >> s1);
}
}
}
# undef png_ptr
}
#endif /* WRITE_SHIFT */
#ifdef PNG_READ_SHIFT_SUPPORTED
/* Reverse the effects of png_do_shift. This routine merely shifts the
* pixels back to their significant bits values. Thus, if you have
* a row of bit depth 8, but only 5 are significant, this will shift
* the values back to 0 through 31.
*/
static void
png_do_unshift(png_transformp *transform, png_transform_controlp tc)
{
# define png_ptr (tc->png_ptr)
png_transform_shift *tr =
png_transform_cast(png_transform_shift, *transform);
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
png_bytep dp = png_voidcast(png_bytep, tc->dp);
png_const_bytep dp_end = dp + PNG_TC_ROWBYTES(*tc);
png_debug(1, "in png_do_unshift");
tc->range++;
tc->format |= PNG_FORMAT_FLAG_RANGE;
{
int shift[4];
unsigned int channels = set_shifts(tc->format, tc->bit_depth,
&tr->true_bits, shift, NULL);
debug(PNG_TC_CHANNELS(*tc) == channels);
{
unsigned int c, have_shift;
for (c = have_shift = 0; c < channels; ++c)
{
/* A shift of more than the bit depth is an error condition but it
* gets ignored here.
*/
if (shift[c] <= 0 || (unsigned)/*SAFE*/shift[c] >= tc->bit_depth)
shift[c] = 0;
else
have_shift = 1;
}
if (have_shift == 0)
return;
}
/* The code below will copy sp to dp, so: */
tc->sp = dp;
switch (tc->bit_depth)
{
default:
/* Must be 1bpp gray: should not be here! */
impossible("unshift bit depth");
/* NOTREACHED */
break;
case 2:
/* Must be 2bpp gray */
debug(channels == 1 && shift[0] == 1);
debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED));
while (dp < dp_end)
*dp++ = (*sp++ >> 1) & 0x55;
break;
case 4:
/* Must be 4bpp gray */
debug(channels == 1);
debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED));
{
unsigned int gray_shift = shift[0];
unsigned int mask = 0xf >> gray_shift; /* <= 4 bits */
mask |= mask << 4; /* <= 8 bits */
while (dp < dp_end)
*dp++ = (png_byte)/*SAFE*/((*sp++ >> gray_shift) & mask);
}
break;
case 8:
/* Single byte components, G, GA, RGB, RGBA */
{
unsigned int channel = 0;
while (dp < dp_end)
{
*dp++ = (png_byte)/*SAFE*/(*sp++ >> shift[channel]);
if (++channel >= channels)
channel = 0;
}
}
break;
case 16:
/* Double byte components, G, GA, RGB, RGBA */
{
unsigned int channel = 0;
unsigned int s0, s1;
if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0)
s0 = 0, s1 = 8; /* LSB */
else
s0 = 8, s1 = 0; /* MSB */
while (dp < dp_end)
{
unsigned int value = *sp++ << s0;
value += *sp++ << s1; /* <= 16 bits */
value >>= shift[channel];
if (++channel >= channels) channel = 0;
*dp++ = PNG_BYTE(value >> s0);
*dp++ = PNG_BYTE(value >> s1);
}
}
break;
}
}
# undef png_ptr
}
#endif /* READ_SHIFT */
static void
init_shift(png_transformp *transform, png_transform_controlp tc)
{
png_const_structp png_ptr = tc->png_ptr;
/* These shifts apply to the component value, not the pixel index, so skip
* palette data. In addition there is no *write* shift for palette entries;
* only a read one, so skip the write/palette case too.
*/
if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0 &&
(png_ptr->read_struct || !tc->palette))
{
/* The only change to the format is to mark the data as having a non-PNG
* range.
*/
tc->range++;
tc->format |= PNG_FORMAT_FLAG_RANGE;
if (tc->init == PNG_TC_INIT_FINAL)
{
# ifdef PNG_READ_SHIFT_SUPPORTED
if (png_ptr->read_struct)
{
(*transform)->fn = png_do_unshift;
return;
}
# endif
# ifdef PNG_WRITE_SHIFT_SUPPORTED
if (png_ptr->read_struct)
{
(*transform)->fn = png_do_shift;
return;
}
# endif
}
}
else /* transform not applicable */
(*transform)->fn = NULL;
}
void PNGAPI
png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits)
{
if (png_ptr != NULL && true_bits != NULL)
{
# ifndef PNG_READ_SHIFT_SUPPORTED
if (png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_shift not supported on read");
return;
}
# endif
# ifndef PNG_WRITE_SHIFT_SUPPORTED
if (!png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_shift not supported on write");
return;
}
# endif
{
png_transform_shift *trs = png_transform_cast(png_transform_shift,
png_add_transform(png_ptr, sizeof (png_transform_shift),
init_shift, PNG_TR_SHIFT));
trs->true_bits = *true_bits;
}
}
}
#endif /* SHIFT */
#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
/* Turn on pixel packing */
void PNGAPI
png_set_packing(png_structrp png_ptr)
{
/* The transforms aren't symmetric, so even though there is one API there are
* two internal init functions, one for read, the other write:
*/
if (png_ptr != NULL)
{
if (png_ptr->read_struct)
{
# ifdef PNG_READ_PACK_SUPPORTED
png_add_transform(png_ptr, 0/*size*/, png_init_read_pack,
PNG_TR_PACK);
# else
png_app_error(png_ptr, "png_set_packing not supported on read");
# endif
}
else
{
# ifdef PNG_WRITE_PACK_SUPPORTED
png_add_transform(png_ptr, 0/*size*/, png_init_write_pack,
PNG_TR_PACK);
# else
png_app_error(png_ptr, "png_set_packing not supported on write");
# endif
}
}
}
#endif /* PACK */
#if defined(PNG_READ_PACKSWAP_SUPPORTED) ||\
defined(PNG_WRITE_PACKSWAP_SUPPORTED)
/* Turn on pixel-swapping within a byte, this is symmetric - doing the swap
* twice produces the original value, so only one implementation is required for
* either read or write.
*
* Used to be refered to as "packswap", but pixel-swap seems more
* self-documenting.
*/
static void
png_do_swap_1bit(png_transformp *transform, png_transform_controlp tc)
{
png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */
png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
tc->sp = dp;
tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
for (;;)
{
png_uint_32 s = *sp++;
s = ((s >> 1) & 0x55555555) | ((s & 0x55555555) << 1);
s = ((s >> 2) & 0x33333333) | ((s & 0x33333333) << 2);
s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4);
*dp++ = s;
if (rowbytes <= 4) break;
rowbytes -= 4;
}
PNG_UNUSED(transform)
}
static void
png_do_swap_2bit(png_transformp *transform, png_transform_controlp tc)
{
png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */
png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
tc->sp = dp;
tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
for (;;)
{
png_uint_32 s = *sp++;
s = ((s >> 2) & 0x33333333) | ((s & 0x33333333) << 2);
s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4);
*dp++ = s;
if (rowbytes <= 4) break;
rowbytes -= 4;
}
PNG_UNUSED(transform)
}
static void
png_do_swap_4bit(png_transformp *transform, png_transform_controlp tc)
{
png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */
png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
tc->sp = dp;
tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
for (;;)
{
png_uint_32 s = *sp++;
s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4);
*dp++ = s;
if (rowbytes <= 4) break;
rowbytes -= 4;
}
PNG_UNUSED(transform)
}
static void
init_packswap(png_transformp *transform, png_transform_controlp tc)
{
png_transform_fn fn;
# define png_ptr tc->png_ptr
debug(tc->init);
# undef png_ptr
switch (tc->bit_depth)
{
case 1: fn = png_do_swap_1bit; break;
case 2: fn = png_do_swap_2bit; break;
case 4: fn = png_do_swap_4bit; break;
default: /* transform not applicable */
(*transform)->fn = NULL;
return;
}
tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
if (tc->init == PNG_TC_INIT_FINAL)
(*transform)->fn = fn;
}
void PNGAPI
png_set_packswap(png_structrp png_ptr)
{
if (png_ptr != NULL)
{
# ifndef PNG_READ_PACKSWAP_SUPPORTED
if (png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_packswap not supported on read");
return;
}
# endif
# ifndef PNG_WRITE_PACKSWAP_SUPPORTED
if (!png_ptr->read_struct)
{
png_app_error(png_ptr, "png_set_packswap not supported on write");
return;
}
# endif
png_add_transform(png_ptr, 0/*size*/, init_packswap, PNG_TR_PIXEL_SWAP);
}
}
#endif /* PACKSWAP */
/* User transform handling */
#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
png_uint_32 PNGAPI
png_get_current_row_number(png_const_structrp png_ptr)
{
/* See the comments in png.h - this is the sub-image row when reading an
* interlaced image.
*/
if (png_ptr != NULL)
{
/* In the read case png_struct::row_number is the row in the final image,
* not the pass, this will return the previous row number if the row isn't
* in the pass:
*/
if (png_ptr->read_struct)
return PNG_PASS_ROWS(png_ptr->row_number+1, png_ptr->pass)-1U;
else
return png_ptr->row_number;
}
return PNG_UINT_32_MAX; /* help the app not to fail silently */
}
png_byte PNGAPI
png_get_current_pass_number(png_const_structrp png_ptr)
{
if (png_ptr != NULL)
return png_ptr->pass;
return 8; /* invalid */
}
#endif /* USER_TRANSFORM_INFO */
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) ||\
defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
typedef struct
{
png_transform tr;
png_user_transform_ptr user_fn;
#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
png_voidp user_ptr;
#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
unsigned int user_depth;
unsigned int user_channels;
#endif
#endif
} png_user_transform, *png_user_transformp;
typedef const png_user_transform *png_const_user_transformp;
static png_user_transformp
get_user_transform(png_structrp png_ptr)
{
/* Note that in an added transform the whole transform is memset to 0, so we
* don't need to initialize anything.
*/
return png_transform_cast(png_user_transform, png_add_transform(png_ptr,
sizeof (png_user_transform), NULL/*function*/, PNG_TR_USER));
}
#endif /* READ_USER_TRANSFORM || WRITE_USER_TRANSFORM */
#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
png_voidp PNGAPI
png_get_user_transform_ptr(png_const_structrp png_ptr)
{
if (png_ptr != NULL)
{
png_transformp tr = png_find_transform(png_ptr, PNG_TR_USER);
if (tr != NULL)
{
png_user_transformp tru = png_transform_cast(png_user_transform, tr);
return tru->user_ptr;
}
}
return NULL;
}
#endif /* USER_TRANSFORM_PTR */
#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
void PNGAPI
png_set_user_transform_info(png_structrp png_ptr, png_voidp ptr, int depth,
int channels)
{
if (png_ptr != NULL)
{
/* NOTE: this function only sets the user transform pointer on write, i.e.
* the depth and channels arguments are ignored.
*/
png_user_transformp tr = get_user_transform(png_ptr);
tr->user_ptr = ptr;
# ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
if (png_ptr->read_struct)
{
if (png_ptr->row_bit_depth == 0)
{
if (depth > 0 && depth <= 32 && channels > 0 && channels <= 4 &&
(-depth & depth) == depth /* power of 2 */)
{
tr->user_depth = png_check_bits(png_ptr, depth, 6);
tr->user_channels = png_check_bits(png_ptr, channels, 3);
}
else
png_app_error(png_ptr, "unsupported bit-depth or channels");
}
else
png_app_error(png_ptr,
"cannot change user info after image start");
}
# else /* !READ_USER_TRANSFORM */
PNG_UNUSED(depth)
PNG_UNUSED(channels)
# endif /* !READ_USER_TRANSFORM */
}
}
#endif /* USER_TRANSFORM_PTR */
#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
static void
png_do_read_user_transform(png_transformp *trIn, png_transform_controlp tc)
{
# define png_ptr (tc->png_ptr)
png_user_transformp tr = png_transform_cast(png_user_transform, *trIn);
if (!tc->init && tr->user_fn != NULL)
{
png_row_info row_info;
row_info.width = tc->width;
row_info.rowbytes = PNG_TC_ROWBYTES(*tc);
row_info.color_type = png_check_byte(png_ptr,
PNG_COLOR_TYPE_FROM_FORMAT(tc->format));
row_info.bit_depth = png_check_byte(png_ptr, tc->bit_depth);
row_info.channels = png_check_byte(png_ptr,
PNG_FORMAT_CHANNELS(tc->format));
row_info.bit_depth = png_check_byte(png_ptr,
PNG_TC_PIXEL_DEPTH(*tc));
/* TODO: fix this API, but for the moment we have to copy the row data to
* the working buffer. This is an unnecessary perf overhead when a user
* transform is used to read information without a transform or when it is
* used on write.
*/
if (tc->sp != tc->dp)
{
memcpy(tc->dp, tc->sp, PNG_TC_ROWBYTES(*tc));
tc->sp = tc->dp;
}
tr->user_fn(png_ptr, &row_info, png_voidcast(png_bytep, tc->dp));
}
# ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
if (tr->user_depth > 0)
{
/* The read transform can modify the bit depth and number of
* channels; the interface doesn't permit anything else to be
* changed. If the information isn't set the user callback has to
* produce pixels with the correct pixel depth (otherwise the
* de-interlace won't work) but there really is no other constraint.
*/
tc->bit_depth = tr->user_depth;
/* The API is very restricted in functionality; the user_channels
* can be changed, but the color_type can't, so the format is simply
* fixed up to match the channels.
*/
if (tr->user_channels != PNG_FORMAT_CHANNELS(tc->format))
switch (tr->user_channels)
{
case 1:
tc->format &=
PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA);
break;
case 2: /* has to be GA */
tc->format &=
PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP+PNG_FORMAT_FLAG_COLOR);
tc->format |= PNG_FORMAT_FLAG_ALPHA;
break;
case 3: /* has to be RGB */
tc->format &=
PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP|PNG_FORMAT_FLAG_ALPHA);
tc->format |= PNG_FORMAT_FLAG_COLOR;
break;
case 4: /* has to be RGBA */
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP);
tc->format |= (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA);
break;
default: /* checked before */
impossible("user channels");
}
debug(PNG_FORMAT_CHANNELS(tc->format) == tr->user_channels);
}
# endif /* USER_TRANSFORM_PTR */
# undef png_ptr
}
void PNGAPI
png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
read_user_transform_fn)
{
/* There is no 'init' function, just the callback above to handle the
* transform.
*/
if (png_ptr != NULL)
{
if (png_ptr->read_struct)
{
png_user_transformp tr = get_user_transform(png_ptr);
tr->user_fn = read_user_transform_fn;
tr->tr.fn = png_do_read_user_transform;
}
else
png_app_error(png_ptr, "cannot set a read transform on write");
}
}
#endif /* READ_USER_TRANSFORM */
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
static void
png_do_write_user_transform(png_transformp *trIn, png_transform_controlp tc)
{
# define png_ptr (tc->png_ptr)
/* The write side is pretty simple; call the call-back, it will make the row
* data right.
*
* BUG: we need to copy the input row (it would be a non-quiet API change
* otherwise) and we don't know how big it is because the information passed
* to png_set_user_transform_info never was used on write, but that's fine
* because the write code never did get this right, so presumably all the
* apps have worked round it...
*/
if (!tc->init)
{
png_user_transformp tr = png_transform_cast(png_user_transform, *trIn);
png_row_info row_info;
if (tc->sp != tc->dp) /* no interlace */
{
memcpy(tc->dp, tc->sp, PNG_TC_ROWBYTES(*tc));
tc->sp = tc->dp;
}
row_info.width = tc->width;
row_info.rowbytes = PNG_TC_ROWBYTES(*tc);
row_info.color_type = png_check_byte(png_ptr,
PNG_COLOR_TYPE_FROM_FORMAT(tc->format));
row_info.bit_depth = png_check_byte(png_ptr, tc->bit_depth);
row_info.channels = png_check_byte(png_ptr,
PNG_FORMAT_CHANNELS(tc->format));
row_info.bit_depth = png_check_byte(png_ptr,
PNG_TC_PIXEL_DEPTH(*tc));
/* The user function promises to give us this format: */
tr->user_fn(png_ptr, &row_info, png_voidcast(png_bytep, tc->dp));
}
# undef png_ptr
}
void PNGAPI
png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
write_user_transform_fn)
{
if (png_ptr != NULL)
{
if (!png_ptr->read_struct)
{
png_user_transformp tr = get_user_transform(png_ptr);
tr->user_fn = write_user_transform_fn;
tr->tr.fn = png_do_write_user_transform;
}
else
png_app_error(png_ptr, "cannot set a write transform on read");
}
}
#endif /* WRITE_USER_TRANSFORM */