#include "pngpriv.h"
#define PNG_SRC_FILE PNG_SRC_FILE_pngtrans
#ifdef _XOPEN_SOURCE
# include <unistd.h>
#endif
#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
# endif
# 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
PNG_UNUSED(png_ptr)
# endif
return 0;
}
#endif
unsigned int PNGAPI
png_memory_format(png_structrp png_ptr)
{
if (png_ptr != NULL)
{
# ifdef PNG_TRANSFORM_MECH_SUPPORTED
unsigned int format = png_ptr->row_format;
# else
unsigned int format = PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type);
# endif
if (png_ptr->read_struct)
{
# ifdef PNG_GAMMA_SUPPORTED
# ifdef PNG_TRANSFORM_MECH_SUPPORTED
unsigned int bit_depth = png_ptr->row_bit_depth;
# else
unsigned int bit_depth = png_ptr->bit_depth;
# endif
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;
if (sex.u8[0] == ((format & PNG_FORMAT_FLAG_SWAPPED) != 0))
break;
}
default:
format |= PNG_FORMAT_FLAG_INVALID;
break;
}
# else
format |= PNG_FORMAT_FLAG_INVALID;
# endif
}
return format;
}
return 0;
}
unsigned int PNGAPI png_memory_channel_depth(png_structrp png_ptr)
{
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)
{
return (png_ptr != NULL) ? memory_gamma(png_ptr) : 0;
}
#endif
#ifdef PNG_TRANSFORM_MECH_SUPPORTED
png_voidp
png_transform_cast_check(png_const_structp png_ptr, unsigned int src_line,
png_transformp tr, size_t size)
{
if (tr->size != size)
png_affirm(png_ptr, param_deb("transform upcast") src_line);
return tr;
}
void
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;
}
}
void
png_init_transform_control(png_transform_controlp tc, png_structp png_ptr)
{
png_byte bd;
png_byte cd;
memset(tc, 0, sizeof *tc);
tc->png_ptr = png_ptr;
tc->sp = tc->dp = NULL;
tc->width = 0;
# ifdef PNG_READ_GAMMA_SUPPORTED
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
{
debug(png_ptr->row_gamma == 0);
}
# endif
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;
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
{
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;
}
}
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)
tc->invalid_info = PNG_INFO_sBIT;
}
# else
tc->invalid_info = PNG_INFO_sBIT;
# endif
}
png_transformp
png_add_transform(png_structrp png_ptr, size_t size, png_transform_fn fn,
unsigned int order)
{
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);
t->next = *p;
t->fn = fn;
t->free = NULL;
t->order = order;
t->size = 0xFFFFU & size;
*p = t;
return t;
}
else
{
affirm((*p)->fn == fn && (*p)->order == order && (*p)->size == size);
return *p;
}
}
png_transformp
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;
affirm(fn != NULL && tr->fn != fn);
{
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)
{
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
static void
remove_transform(png_const_structp png_ptr, png_transformp *transform)
{
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
png_remove_transform(png_const_structp png_ptr, png_transformp *transform)
{
remove_transform(png_ptr, transform);
}
#endif
static unsigned int
run_transform_list_forwards(png_transform_controlp tc, png_transformp *start,
png_transformp end)
{
png_const_structp png_ptr = tc->png_ptr;
unsigned int max_depth = PNG_TC_PIXEL_DEPTH(*tc);
debug(*start != NULL);
do
{
if ((*start)->fn != NULL)
(*start)->fn(start, tc);
if ((*start)->fn == NULL)
remove_transform(png_ptr, start);
else
{
unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(*tc);
if (tc_depth > max_depth)
max_depth = tc_depth;
start = &(*start)->next;
}
}
while (*start != NULL && *start != end);
debug(*start == end);
return max_depth;
}
#ifdef PNG_READ_TRANSFORMS_SUPPORTED
unsigned int
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
#ifdef PNG_READ_SUPPORTED
unsigned int
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
#ifdef PNG_WRITE_SUPPORTED
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);
if ((*list)->fn != NULL)
(*list)->fn(list, tc);
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
png_run_transform_list_backwards(png_structp png_ptr, png_transform_controlp tc)
{
if (png_ptr->transform_list != NULL)
{
unsigned int max_depth =
run_transform_list_backwards(tc, &png_ptr->transform_list);
affirm(max_depth <= png_ptr->row_max_pixel_depth);
}
}
#endif
static unsigned int
init_transform_mech(png_structrp png_ptr, png_transform_control *tc, int start)
{
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
#ifdef PNG_PALETTE_MAX_SUPPORTED
static int
set_palette_max(png_structrp png_ptr, png_transformp tr, unsigned int max,
unsigned int format_max)
{
# ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
if (max >= (tr->args & 0x1FFU) && !png_ptr->palette_index_check_issued)
{
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
)
{
# ifdef PNG_READ_SUPPORTED
# ifdef PNG_WRITE_SUPPORTED
if (png_ptr->read_struct)
# endif
png_chunk_benign_error(png_ptr, "palette index too large");
# ifdef PNG_WRITE_SUPPORTED
else
# endif
# endif
# ifdef PNG_WRITE_SUPPORTED
png_error(png_ptr, "palette index too large");
# endif
}
png_ptr->palette_index_check_issued = 1;
}
# endif
# ifdef PNG_GET_PALETTE_MAX_SUPPORTED
png_ptr->palette_index_max = png_check_byte(png_ptr, max);
# endif
if (max == format_max)
{
tr->fn = NULL;
return 1;
}
return 0;
}
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;
}
(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;
while (width > 0)
{
png_uint_32 input = 0U, test;
unsigned int new_max;
while (width > 0)
{
unsigned int next = *sp++;
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;
else if (max < 2U)
new_max = 2U;
else
continue;
}
else if (input != 0 && max == 0)
new_max = 1U;
else
continue;
if (set_palette_max(tc->png_ptr, *tr, new_max, 3U))
return;
max = new_max;
}
(*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;
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;
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)
{
(*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
#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;
return -1;
PNG_UNUSED(info_ptr)
}
#endif
void
png_init_row_info(png_structrp png_ptr)
{
const png_byte png_depth =
png_check_bits(png_ptr, PNG_PIXEL_DEPTH(*png_ptr), 7U);
# ifdef PNG_TRANSFORM_MECH_SUPPORTED
# ifdef PNG_PALETTE_MAX_SUPPORTED
if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && (
# 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
)
# else
0
# endif
||
# 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
)
# else
0
# endif
))
png_add_transform(png_ptr, 0, palette_max_init,
PNG_TR_CHECK_PALETTE);
# endif
if (png_ptr->transform_list != NULL)
{
png_transform_control tc;
(void)init_transform_mech(png_ptr, &tc, 1);
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
if (png_ptr->transform_list != NULL)
{
unsigned int max_depth =
init_transform_mech(png_ptr, &tc, 0);
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
png_ptr->invalid_info = tc.invalid_info;
# endif
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
png_ptr->row_max_pixel_depth =
png_check_bits(png_ptr, max_depth, 8U);
{
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;
}
}
}
else
{
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
# ifdef PNG_READ_TRANSFORMS_SUPPORTED
png_ptr->invalid_info = 0U;
# endif
}
# endif
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
png_app_error(png_ptr, "no de-interlace support");
# endif
}
else
{
# ifdef PNG_WRITE_INTERLACING_SUPPORTED
if (png_ptr->interlaced)
{
png_ptr->do_interlace = 1;
return PNG_INTERLACE_ADAM7_PASSES;
}
return 1;
# else
png_app_error(png_ptr, "no interlace support");
# endif
}
}
return 0;
}
#endif
#ifdef PNG_MNG_READ_FEATURES_SUPPORTED
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;
do
{
*dp++ = PNG_BYTE(sp[0] + sp[1]);
*dp++ = *++sp;
*dp++ = PNG_BYTE(sp[0] + sp[1]);
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]);
*dp++ = *++sp;
*dp++ = PNG_BYTE(sp[0] + sp[1]);
sp += 2;
*dp++ = *sp++;
}
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;
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;
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++;
}
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)
{
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
#ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED
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;
do
{
*dp++ = PNG_BYTE(sp[0] - sp[1]);
*dp++ = *++sp;
*dp++ = PNG_BYTE(sp[0] - sp[1]);
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]);
*dp++ = *++sp;
*dp++ = PNG_BYTE(sp[0] - sp[1]);
sp += 2;
*dp++ = *sp++;
}
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;
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++;
}
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)
{
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
#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, png_init_read_intrapixel,
PNG_TR_MNG_INTRAPIXEL);
# else
if (png_ptr->read_struct)
{
png_app_error(png_ptr, "MNG not supported on read");
return;
}
# endif
# ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED
if ((mng_features & PNG_FLAG_MNG_FILTER_64) != 0)
png_add_transform(png_ptr, 0, png_init_write_intrapixel,
PNG_TR_MNG_INTRAPIXEL);
# else
if (!png_ptr->read_struct)
{
png_app_error(png_ptr, "MNG not supported on write");
return;
}
# endif
return png_ptr->mng_features_permitted =
mng_features & PNG_ALL_MNG_FEATURES;
}
return 0;
}
#endif
#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)
typedef struct
{
png_transform tr;
png_uint_32 codes;
unsigned int format;
unsigned int bit_depth;
png_byte filler[4];
} png_transform_byte_op;
static void
png_do_byte_ops_up(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);
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;
{
const png_uint_32 codes = tr->codes;
png_uint_32 code = codes;
unsigned int i, hwm;
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
{
sp += sp_advance;
if (sp >= ep)
break;
code = codes;
continue;
}
code >>= 4;
if (i == hwm)
{
hwm &= 0x10U;
memcpy(dp, output + hwm, 16);
dp += 16;
i = hwm;
hwm += 16;
}
}
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;
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)
{
# 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;
{
const png_uint_32 codes = tr->codes;
png_uint_32 code = codes;
unsigned int size, hwm, i;
png_byte output[32] = { 0 };
affirm((codes & 0xFU) >= 4U);
size = dest_rowbytes & 0xFU;
if (size == 0U) size = 16U;
i = size+16U;
sp -= sp_advance;
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
{
sp -= sp_advance;
if (sp < ep)
break;
code = codes;
continue;
}
code >>= 4;
if (i == hwm)
{
dp -= size;
hwm ^= 0x10U;
memcpy(dp, output + hwm, size);
size = 16U;
if (i == 0U) i = 32U;
}
}
debug((i == 16U || i == 32U) & (((i & 0x10U)^0x10U) == hwm));
debug(sp+sp_advance == ep);
if (size < 16U)
{
debug(i == 16U);
dp -= size;
memcpy(dp, output + i, size);
}
else
{
debug(size == 16U);
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
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
debug((rowbytes & 1) == 0);
swab(sp, dp, rowbytes);
# else
{
const png_const_bytep ep = sp + rowbytes - 1;
while (sp < ep)
{
png_byte b0 = *sp++;
*dp++ = *sp++;
*dp++ = b0;
}
debug(sp == ep+1);
}
# endif
PNG_UNUSED(transform)
# undef png_ptr
}
#define PNG_BO_STRIP_ALPHA 0x0001U
#define PNG_BO_CHOP_16_TO_8 0x0002U
#define PNG_BO_GRAY_TO_RGB 0x0004U
#define PNG_BO_EXPAND_16 0x0008U
#define PNG_BO_BGR 0x0010U
#define PNG_BO_FILLER 0x0020U
#define PNG_BO_SWAP_ALPHA 0x0040U
#define PNG_BO_SWAP_16 0x0080U
#define PNG_BO_FILLER_ALPHA 0x4000U
#define PNG_BO_FILLER_FIRST 0x8000U
static void
png_init_byte_ops(png_transformp *transform, png_transform_controlp tc)
{
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;
const unsigned int png_bit_depth = tc->bit_depth;
unsigned int bit_depth = png_bit_depth;
debug(tc->init);
if ((png_format & PNG_FORMAT_FLAG_COLORMAP) != 0U || png_bit_depth < 8U)
{
tr->tr.fn = NULL;
return;
}
if (tr->tr.next != NULL && tr->tr.next->order == PNG_TR_CHANNEL_POSTQ)
{
debug(tr->tr.order == PNG_TR_CHANNEL_PREQ);
debug((args & tr->tr.next->args) == 0U);
tr->tr.next->args |= args;
tr->tr.fn = NULL;
return;
}
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)
{
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;
tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL;
}
else
args &= PNG_BIC_MASK(PNG_BO_CHOP_16_TO_8);
}
if ((args & PNG_BO_EXPAND_16) != 0U)
{
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)
{
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;
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))
format |= PNG_FORMAT_FLAG_AFILLER;
}
else
args &= PNG_BIC_MASK(PNG_BO_FILLER);
}
if ((args & PNG_BO_SWAP_ALPHA) != 0U)
{
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)
{
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];
codes[0] = 8U;
codes[1] = 9U;
codes[2] = 10U;
codes[3] = 11U;
codes[7] = codes[6] = codes[5] = codes[4] = 0U;
if ((args & PNG_BO_GRAY_TO_RGB) != 0U)
{
codes[3] = 9U;
codes[2] = codes[1] = 8U;
# ifdef PNG_READ_tRNS_SUPPORTED
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
}
else if ((args & PNG_BO_BGR) != 0U)
{
codes[0] = 10U;
codes[2] = 8U;
}
if ((args & PNG_BO_FILLER) != 0U)
{
if ((format & PNG_FORMAT_FLAG_COLOR) != 0U)
codes[3] = 4U;
else
codes[1] = 4U;
}
if ((args & PNG_BO_SWAP_ALPHA) != 0U)
{
if ((format & PNG_FORMAT_FLAG_COLOR) != 0U)
{
png_byte acode = codes[3];
codes[3] = codes[2];
codes[2] = codes[1];
codes[1] = codes[0];
codes[0] = acode;
}
else
codes[0] = codes[1], codes[1] = 8U;
}
if ((args & PNG_BO_CHOP_16_TO_8) != 0U)
{
unsigned int i = PNG_FORMAT_CHANNELS(format);
while (i > 0U)
{
unsigned int code = codes[--i];
if (code > 8U)
codes[i] = PNG_BYTE(8U+2U*(code-8U));
}
}
if ((args & PNG_BO_EXPAND_16) != 0U)
{
unsigned int i = PNG_FORMAT_CHANNELS(format);
while (i > 0U)
{
png_byte code = codes[--i];
if (code == 4U)
codes[2U*i] = 5U, codes[2U*i+1U] = 4U;
else
codes[2U*i] = codes[2U*i+1U] = code;
}
# ifdef PNG_READ_tRNS_SUPPORTED
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
}
else if (bit_depth == 16U)
{
unsigned int i = PNG_FORMAT_CHANNELS(format);
while (i > 0U)
{
unsigned int code = codes[--i];
if (code == 4U)
codes[2U*i] = 5U, codes[2U*i+1U] = 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)
{
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
if (!png_ptr->read_struct)
{
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)
write_codes[code-8U] = PNG_BYTE(8U+i);
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;
go_down = png_pixel_size > memory_pixel_size;
affirm(!go_down);
memcpy(codes, write_codes, sizeof codes);
}
else
# endif
{
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;
}
tr->tr.args = args;
if (!go_down)
{
if (memory_pixel_size == png_pixel_size)
{
int the_same = 1;
int swapped = (memory_pixel_size & 1) == 0;
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
{
swapped = 0;
if (!the_same)
break;
}
}
if (swapped)
{
tr->tr.fn = png_do_bswap;
return;
}
else if (the_same)
impossible("not reached");
}
tr->tr.fn = png_do_byte_ops_up;
{
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
# ifdef PNG_READ_TRANSFORMS_SUPPORTED
{
tr->tr.fn = png_do_byte_ops_down;
{
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
impossible("not reached");
# endif
}
}
else
{
tc->format = format;
tc->bit_depth = bit_depth;
tr->tr.args = args;
}
}
else
tr->tr.fn = NULL;
}
#endif
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
static void
png_init_rgb_to_gray_byte_ops(png_transformp *transform,
png_transform_controlp tc)
{
(*transform)->fn = png_do_byte_ops_up;
if (!tc->init)
png_do_byte_ops_up(transform, tc);
else
{
# 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
png_add_rgb_to_gray_byte_ops(png_structrp png_ptr, png_transform_controlp tc,
unsigned int index, unsigned int 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;
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);
index *= 2U;
tr->codes = (8U + index) + ((9U + index) << 4) +
((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ?
((8U+6U) + ((9U+6U) << 4)) << 8 : 0U);
}
}
#endif
#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) &&\
defined(PNG_READ_BACKGROUND_SUPPORTED)
void
png_push_gray_to_rgb_byte_ops(png_transformp *transform,
png_transform_controlp tc)
{
# 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
#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
void
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
#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
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
#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
#ifdef PNG_READ_EXPAND_16_SUPPORTED
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
#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
#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
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;
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);
}
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);
}
#endif
#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
#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
#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)
{
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);
# 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
#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\
defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) ||\
defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
#define PNG_B_INVERT_MONO 1U
#define PNG_B_INVERT_RGB 2U
#define PNG_B_INVERT_ALPHA 4U
typedef struct
{
png_transform tr;
unsigned int step0;
unsigned int step;
png_uint_32 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;
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;
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
{
if (step0)
*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;
if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
present = 0;
else
{
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;
}
invert &= present;
if (invert == 0)
(*transform)->fn = NULL;
else
{
tc->format |= PNG_FORMAT_FLAG_RANGE;
tc->range++;
if (tc->init == PNG_TC_INIT_FINAL)
{
if (invert == present)
(*transform)->fn = png_do_invert_all;
else
{
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);
{
union
{
png_byte bytes[8];
png_uint_32 words[2];
} masks;
memset(&masks, 0, sizeof masks);
if (tc->bit_depth == 8)
{
masks.bytes[channel+channels] = masks.bytes[channel] = 0xff;
tr->step = 1;
tr->mask = masks.words[0];
tr->step0 = 0;
}
else
{
channel <<= 1;
masks.bytes[channel+1] = masks.bytes[channel] = 0xff;
if (channels == 2)
{
tr->step = 1;
tr->mask = masks.words[0];
tr->step0 = 0;
}
else
{
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
png_ptr->write_invert_alpha = 1U;
# endif
}
}
#endif
#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
#endif
#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
typedef struct
{
png_transform tr;
png_color_8 true_bits;
} png_transform_shift;
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;
if ((format & PNG_FORMAT_FLAG_COLOR) != 0)
{
unsigned int offset =
((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;
shift_start[channels+offset] = bit_depth - true_bits->blue;
if (shift_dec != NULL) shift_dec[channels+offset] = true_bits->blue;
channels += 3;
}
else
{
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);
if (tc->bit_depth < 8)
{
unsigned int mask;
UNTESTED
affirm(channels == 1);
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
{
unsigned int c = 0, s0, s1;
UNTESTED
if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0)
s0 = 0, s1 = 8;
else
s0 = 8, s1 = 0;
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
#ifdef PNG_READ_SHIFT_SUPPORTED
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)
{
if (shift[c] <= 0 || (unsigned)shift[c] >= tc->bit_depth)
shift[c] = 0;
else
have_shift = 1;
}
if (have_shift == 0)
return;
}
tc->sp = dp;
switch (tc->bit_depth)
{
default:
impossible("unshift bit depth");
break;
case 2:
debug(channels == 1 && shift[0] == 1);
debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED));
while (dp < dp_end)
*dp++ = (*sp++ >> 1) & 0x55;
break;
case 4:
debug(channels == 1);
debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED));
{
unsigned int gray_shift = shift[0];
unsigned int mask = 0xf >> gray_shift;
mask |= mask << 4;
while (dp < dp_end)
*dp++ = (png_byte)((*sp++ >> gray_shift) & mask);
}
break;
case 8:
{
unsigned int channel = 0;
while (dp < dp_end)
{
*dp++ = (png_byte)(*sp++ >> shift[channel]);
if (++channel >= channels)
channel = 0;
}
}
break;
case 16:
{
unsigned int channel = 0;
unsigned int s0, s1;
if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0)
s0 = 0, s1 = 8;
else
s0 = 8, s1 = 0;
while (dp < dp_end)
{
unsigned int value = *sp++ << s0;
value += *sp++ << s1;
value >>= shift[channel];
if (++channel >= channels) channel = 0;
*dp++ = PNG_BYTE(value >> s0);
*dp++ = PNG_BYTE(value >> s1);
}
}
break;
}
}
# undef png_ptr
}
#endif
static void
init_shift(png_transformp *transform, png_transform_controlp tc)
{
png_const_structp png_ptr = tc->png_ptr;
if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0 &&
(png_ptr->read_struct || !tc->palette))
{
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)->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
#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
void PNGAPI
png_set_packing(png_structrp png_ptr)
{
if (png_ptr != NULL)
{
if (png_ptr->read_struct)
{
# ifdef PNG_READ_PACK_SUPPORTED
png_add_transform(png_ptr, 0, 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, png_init_write_pack,
PNG_TR_PACK);
# else
png_app_error(png_ptr, "png_set_packing not supported on write");
# endif
}
}
}
#endif
#if defined(PNG_READ_PACKSWAP_SUPPORTED) ||\
defined(PNG_WRITE_PACKSWAP_SUPPORTED)
static void
png_do_swap_1bit(png_transformp *transform, png_transform_controlp tc)
{
png_alloc_size_t rowbytes = row_align(tc);
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);
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);
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)->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, init_packswap, PNG_TR_PIXEL_SWAP);
}
}
#endif
#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
png_uint_32 PNGAPI
png_get_current_row_number(png_const_structrp png_ptr)
{
if (png_ptr != NULL)
{
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;
}
png_byte PNGAPI
png_get_current_pass_number(png_const_structrp png_ptr)
{
if (png_ptr != NULL)
return png_ptr->pass;
return 8;
}
#endif
#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)
{
return png_transform_cast(png_user_transform, png_add_transform(png_ptr,
sizeof (png_user_transform), NULL, PNG_TR_USER));
}
#endif
#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
#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)
{
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 )
{
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
PNG_UNUSED(depth)
PNG_UNUSED(channels)
# endif
}
}
#endif
#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));
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)
{
tc->bit_depth = tr->user_depth;
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:
tc->format &=
PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP+PNG_FORMAT_FLAG_COLOR);
tc->format |= PNG_FORMAT_FLAG_ALPHA;
break;
case 3:
tc->format &=
PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP|PNG_FORMAT_FLAG_ALPHA);
tc->format |= PNG_FORMAT_FLAG_COLOR;
break;
case 4:
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP);
tc->format |= (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA);
break;
default:
impossible("user channels");
}
debug(PNG_FORMAT_CHANNELS(tc->format) == tr->user_channels);
}
# endif
# undef png_ptr
}
void PNGAPI
png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
read_user_transform_fn)
{
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
#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)
if (!tc->init)
{
png_user_transformp tr = png_transform_cast(png_user_transform, *trIn);
png_row_info row_info;
if (tc->sp != tc->dp)
{
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));
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