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