libpng/pngtrans.c
John Bowler 01ff090760 API reduction, PNG compression level
PNG compression level setting API: this allows the various compression settings
controlling deflate, fitlering, and so on to be set via a single setting with
six values.  This is currently documented in png.h ("Write compression
settings").

Internally the compression settings have been tuned both for the overall setting
and for any specific settings made by the original APIs.

APIs to control iCCP chunk compression separately have been added.

contrib/examples/pngcp.c has been modified to accomodate the new compression
setting and to include options for separate control of iCCP chunk compression.

The new ABI, png_setting, has been modified to accomodate a wider range of
settings and most of the old compression control ABIs have been replaced by
function-like macros with the same API which call png_setting.  This is an API
check in 1.7.0 for png_setting (alone).  png_setting now handles all of
png_set_option.  This eliminates 19 ABIs at the cost of adding 1 (png_setting).

CRC and benign error checking has been updated internally to use bit-fields and
the CRC calculation skip when the CRC is not used has been improved slightly to
avoid the initialization of the CRC.  A new png_setting based API allows more
detailed control of benign error/warning messages (this may change, the internal
error handling seems too complex.)  The ERROR_NUMBERS support has been removed
with the intent of implementing proper i18n.

The memcpy-size-0 issue in png_push_fill_buffer has been fixed, with an
appropriate debug() assert if a fill for 0 bytes occurs.

Most PNG_FLAG_ values for png_struct::flags have been eliminated (as a result of
the benign error handling changes).  Only one remains.

Signed-off-by: John Bowler <jbowler@acm.org>
2016-06-07 06:48:52 -07:00

3757 lines
113 KiB
C

/* 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
(png_ptr->read_struct ? png_chunk_benign_error : png_error)
# else /* !WRITE */
png_chunk_benign_error
# endif /* READ */
# else /* !READ */
png_error
# endif /* WRITE */
(png_ptr, "palette index too large");
png_ptr->palette_index_check_issued = 1;
}
# endif
# 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 */