mirror of
https://git.code.sf.net/p/libpng/code.git
synced 2025-07-10 18:04:09 +02:00

The internal read code change to stop sharing the palette was incompletely implemented. The result is that unless palette index checking is turned off and there are no read transformations the png_info palette gets deleted when the png_struct is deleted. This is normally harmless (png_info gets deleted first) but in the case of pngcp it results in use-after-free of the palette and, therefore, palette corruption and maybe on some operating systems and access violation. This also updated pngcp 'search' mode to check a restricted range of memLevels; there is an unrelated bug which means that lower zlib memLevels result in memory corruption under some circumstances, probably less often than 1:1000. Signed-off-by: John Bowler <jbowler@acm.org>
6776 lines
218 KiB
C
6776 lines
218 KiB
C
|
|
/* pngrtran.c - transforms the data in a row for PNG readers
|
|
*
|
|
* 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
|
|
*
|
|
* This file contains functions optionally called by an application
|
|
* in order to tell libpng how to handle data when reading a PNG.
|
|
* Transformations that are used in both reading and writing are
|
|
* in pngtrans.c.
|
|
*/
|
|
|
|
#include "pngpriv.h"
|
|
#define PNG_SRC_FILE PNG_SRC_FILE_pngrtran
|
|
|
|
#ifdef PNG_READ_QUANTIZE_SUPPORTED
|
|
typedef struct
|
|
{
|
|
png_transform tr;
|
|
png_byte map[256U]; /* Map of palette values */
|
|
png_byte lut[1U << /* LUT for RGB values */
|
|
(PNG_QUANTIZE_RED_BITS+PNG_QUANTIZE_GREEN_BITS+PNG_QUANTIZE_BLUE_BITS)];
|
|
} png_transform_quantize;
|
|
|
|
#define PNG_QUANTIZE_MAP 1U /* map is present and not a 1:1 mapping */
|
|
#define PNG_QUANTIZE_LUT 2U /* lut has been built */
|
|
|
|
static void
|
|
do_quantize_rgb(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_quantize *tr = png_transform_cast(png_transform_quantize,
|
|
*transform);
|
|
unsigned int channels = PNG_TC_CHANNELS(*tc);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - channels/*safety*/;
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
|
|
affirm(tc->bit_depth == 8 && (channels == 3 || channels == 4) &&
|
|
!(tc->format & PNG_FORMAT_FLAG_SWAPPED) &&
|
|
(tr->tr.args & PNG_QUANTIZE_LUT) != 0);
|
|
|
|
tc->sp = dp;
|
|
tc->format |= PNG_FORMAT_FLAG_COLORMAP;
|
|
|
|
while (sp <= ep)
|
|
{
|
|
unsigned int r = sp[0];
|
|
unsigned int g = sp[1];
|
|
unsigned int b = sp[2];
|
|
|
|
/* This looks real messy, but the compiler will reduce
|
|
* it down to a reasonable formula. For example, with
|
|
* 5 bits per color, we get:
|
|
* p = (((r >> 3) & 0x1f) << 10) |
|
|
* (((g >> 3) & 0x1f) << 5) |
|
|
* ((b >> 3) & 0x1f);
|
|
*/
|
|
*dp++ = tr->lut[(((r >> (8 - PNG_QUANTIZE_RED_BITS)) &
|
|
((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<
|
|
(PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |
|
|
(((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &
|
|
((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<
|
|
(PNG_QUANTIZE_BLUE_BITS)) |
|
|
((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &
|
|
((1 << PNG_QUANTIZE_BLUE_BITS) - 1))];
|
|
|
|
sp += channels;
|
|
}
|
|
|
|
affirm(sp == ep+channels);
|
|
UNTESTED
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
do_quantize_pal(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_quantize *tr = png_transform_cast(png_transform_quantize,
|
|
*transform);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
|
|
affirm(tc->bit_depth == 8 && (tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0 &&
|
|
!(tc->format & PNG_FORMAT_FLAG_SWAPPED) &&
|
|
(tr->tr.args & PNG_QUANTIZE_MAP) != 0);
|
|
|
|
tc->sp = dp;
|
|
|
|
while (sp < ep)
|
|
*dp++ = tr->map[*sp++];
|
|
|
|
UNTESTED
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_init_quantize(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
if (tc->bit_depth == 8 && (tc->format & PNG_FORMAT_FLAG_COLOR) != 0)
|
|
{
|
|
/* Either colormapped input, RGB or RGBA: */
|
|
if (!(tc->format & PNG_FORMAT_FLAG_COLORMAP)) /* RGB, RGBA */
|
|
{
|
|
/* This must be a 'palette' lookup */
|
|
if (((*transform)->args & PNG_QUANTIZE_LUT) != 0)
|
|
{
|
|
/* This changes the format and invalidates pretty much everything in
|
|
* the info struct:
|
|
*/
|
|
tc->format |= PNG_FORMAT_FLAG_COLORMAP;
|
|
|
|
if (tc->init == PNG_TC_INIT_FINAL)
|
|
{
|
|
(*transform)->fn = do_quantize_rgb;
|
|
tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL+
|
|
PNG_INFO_sBIT+PNG_INFO_bKGD;
|
|
tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
|
|
png_check_byte(tc->png_ptr, tc->bit_depth);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
else /* colormapped */
|
|
{
|
|
/* This must be a 'quantize' lookup */
|
|
if (((*transform)->args & PNG_QUANTIZE_MAP) != 0)
|
|
{
|
|
/* This doesn't change the format, just the values: */
|
|
if (tc->init == PNG_TC_INIT_FINAL)
|
|
{
|
|
(*transform)->fn = do_quantize_pal;
|
|
tc->invalid_info |= PNG_INFO_sBIT+PNG_INFO_pCAL;
|
|
tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
|
|
png_check_byte(tc->png_ptr, tc->bit_depth);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Else not applicable */
|
|
(*transform)->fn = NULL;
|
|
}
|
|
|
|
/* Dither file to 8-bit. Supply a palette, the current number
|
|
* of elements in the palette, the maximum number of elements
|
|
* allowed, and a histogram if possible. If the current number
|
|
* of colors is greater then the maximum number, the palette will be
|
|
* modified to fit in the maximum number. "full_quantize" indicates
|
|
* whether we need a quantizing cube set up for RGB images, or if we
|
|
* simply are reducing the number of colors in a paletted image.
|
|
*/
|
|
typedef struct png_dsort_struct
|
|
{
|
|
struct png_dsort_struct * next;
|
|
png_byte left;
|
|
png_byte right;
|
|
} png_dsort;
|
|
typedef png_dsort * png_dsortp;
|
|
typedef png_dsort * * png_dsortpp;
|
|
|
|
static void
|
|
init_map(png_bytep map)
|
|
/* Initialize a mapping table to be 1:1 */
|
|
{
|
|
png_byte b = 0U;
|
|
|
|
do
|
|
map[b] = b;
|
|
while (b++ != 255U);
|
|
}
|
|
|
|
/* Save typing and make code easier to understand */
|
|
#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
|
|
abs((int)((c1).green) - (int)((c2).green)) + \
|
|
abs((int)((c1).blue) - (int)((c2).blue)))
|
|
|
|
void PNGAPI
|
|
png_set_quantize(png_structrp png_ptr, png_colorp palette,
|
|
int num_palette, int maximum_colors, png_const_uint_16p histogram,
|
|
int full_quantize)
|
|
{
|
|
png_debug(1, "in png_set_quantize");
|
|
|
|
if (png_ptr != NULL)
|
|
{
|
|
png_transform_quantize *tr = png_transform_cast(png_transform_quantize,
|
|
png_add_transform(png_ptr, sizeof (png_transform_quantize),
|
|
png_init_quantize, PNG_TR_QUANTIZE));
|
|
|
|
/* This is weird (consider what happens to png_set_background on a palette
|
|
* image with a tRNS chunk).
|
|
*/
|
|
if (palette == png_ptr->palette)
|
|
png_app_warning(png_ptr, "png_set_quantize: PLTE will be damaged");
|
|
|
|
if (maximum_colors <= 0 || num_palette > 256)
|
|
{
|
|
/* The spuriously allocated transform will be removed by the init
|
|
* code.
|
|
*/
|
|
png_app_error(png_ptr, "png_set_quantize: invalid color count");
|
|
return;
|
|
}
|
|
|
|
/* The app passed in a palette with too many colors, it's not clear why
|
|
* libpng is providing this functionality, it's nothing to do with PNG and
|
|
* can be done by the application without any PNG specific knowledge.
|
|
*/
|
|
if (num_palette > maximum_colors)
|
|
{
|
|
int map_changed = 0;
|
|
|
|
/* The map table must be preset to do no mapping initially: */
|
|
init_map(tr->map);
|
|
|
|
if (histogram != NULL)
|
|
{
|
|
/* This is easy enough, just throw out the least used colors.
|
|
* Perhaps not the best solution, but good enough.
|
|
*/
|
|
int i;
|
|
png_byte quantize_sort[256U];
|
|
|
|
/* Initialize an array to sort colors */
|
|
init_map(quantize_sort);
|
|
|
|
/* Find the least used palette entries by starting a
|
|
* bubble sort, and running it until we have sorted
|
|
* out enough colors. Note that we don't care about
|
|
* sorting all the colors, just finding which are
|
|
* least used.
|
|
*/
|
|
for (i = num_palette - 1; i >= maximum_colors; i--)
|
|
{
|
|
int done; /* To stop early if the list is pre-sorted */
|
|
int j;
|
|
|
|
done = 1;
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
if (histogram[quantize_sort[j]] <
|
|
histogram[quantize_sort[j+1]])
|
|
{
|
|
png_byte t = quantize_sort[j];
|
|
quantize_sort[j] = quantize_sort[j+1];
|
|
quantize_sort[j+1] = t;
|
|
done = 0;
|
|
}
|
|
}
|
|
|
|
if (done != 0)
|
|
break;
|
|
}
|
|
|
|
/* Swap the palette around, and set up a table, if necessary */
|
|
if (full_quantize)
|
|
{
|
|
int j = num_palette;
|
|
|
|
/* Put all the useful colors within the max, but don't
|
|
* move the others.
|
|
*
|
|
* NOTE: if the app passes in the result of png_get_PLTE it will
|
|
* be overwritten at this point, what is the API?
|
|
*/
|
|
for (i = 0; i < maximum_colors; i++)
|
|
{
|
|
if (quantize_sort[i] >= maximum_colors)
|
|
{
|
|
do
|
|
j--;
|
|
while (quantize_sort[j] >= maximum_colors);
|
|
|
|
/* NOTE: NOT swapped, so the original palette[i] has been
|
|
* lost.
|
|
*/
|
|
palette[i] = palette[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
else /* !full_quantize */
|
|
{
|
|
int j = num_palette;
|
|
|
|
/* Move all the used colors inside the max limit, and
|
|
* develop a translation table.
|
|
*/
|
|
for (i = 0; i < maximum_colors; i++)
|
|
{
|
|
/* Only move the colors we need to */
|
|
if (quantize_sort[i] >= maximum_colors)
|
|
{
|
|
png_color tmp_color;
|
|
|
|
do
|
|
j--;
|
|
while (quantize_sort[j] >= maximum_colors);
|
|
|
|
tmp_color = palette[j];
|
|
palette[j] = palette[i];
|
|
palette[i] = tmp_color;
|
|
/* Indicate where the color went */
|
|
tr->map[j] = png_check_byte(png_ptr, i);
|
|
tr->map[i] = png_check_byte(png_ptr, j);
|
|
map_changed = 1;
|
|
}
|
|
}
|
|
|
|
/* Find closest color for those colors we are not using */
|
|
for (i = 0; i < num_palette; i++)
|
|
{
|
|
if (tr->map[i] >= maximum_colors)
|
|
{
|
|
int min_d, k, min_k, d_index;
|
|
|
|
/* Find the closest color to one we threw out */
|
|
d_index = tr->map[i];
|
|
min_d = PNG_COLOR_DIST(palette[d_index], palette[0]);
|
|
for (k = 1, min_k = 0; k < maximum_colors; k++)
|
|
{
|
|
int d;
|
|
|
|
d = PNG_COLOR_DIST(palette[d_index], palette[k]);
|
|
|
|
if (d < min_d)
|
|
{
|
|
min_d = d;
|
|
min_k = k;
|
|
}
|
|
}
|
|
|
|
/* Point to closest color */
|
|
tr->map[i] = png_check_byte(png_ptr, min_k);
|
|
map_changed = 1;
|
|
}
|
|
}
|
|
} /* !full_quantize */
|
|
} /* have a histogram */
|
|
|
|
else /* no histogram */
|
|
{
|
|
/* This is much harder to do simply (and quickly). Perhaps
|
|
* we need to go through a median cut routine, but those
|
|
* don't always behave themselves with only a few colors
|
|
* as input. So we will just find the closest two colors,
|
|
* and throw out one of them (chosen somewhat randomly).
|
|
* [We don't understand this at all, so if someone wants to
|
|
* work on improving it, be our guest - AED, GRP]
|
|
*/
|
|
int max_d;
|
|
int num_new_palette;
|
|
png_byte index_to_palette[256U];
|
|
png_byte palette_to_index[256U];
|
|
png_dsortp hash[769];
|
|
|
|
/* Initialize palette index sort arrays */
|
|
init_map(index_to_palette);
|
|
init_map(palette_to_index);
|
|
memset(hash, 0, sizeof hash);
|
|
num_new_palette = num_palette;
|
|
|
|
/* Initial wild guess at how far apart the farthest pixel
|
|
* pair we will be eliminating will be. Larger
|
|
* numbers mean more areas will be allocated, Smaller
|
|
* numbers run the risk of not saving enough data, and
|
|
* having to do this all over again.
|
|
*
|
|
* I have not done extensive checking on this number.
|
|
*/
|
|
max_d = 96;
|
|
|
|
while (num_new_palette > maximum_colors)
|
|
{
|
|
int i;
|
|
png_dsortp t = NULL;
|
|
|
|
for (i = 0; i < num_new_palette - 1; i++)
|
|
{
|
|
int j;
|
|
|
|
for (j = i + 1; j < num_new_palette; j++)
|
|
{
|
|
int d = PNG_COLOR_DIST(palette[i], palette[j]);
|
|
|
|
if (d <= max_d)
|
|
{
|
|
|
|
t = png_voidcast(png_dsortp, png_malloc_warn(png_ptr,
|
|
sizeof (*t)));
|
|
|
|
if (t == NULL)
|
|
break;
|
|
|
|
t->next = hash[d];
|
|
t->left = png_check_byte(png_ptr, i);
|
|
t->right = png_check_byte(png_ptr, j);
|
|
hash[d] = t;
|
|
}
|
|
}
|
|
if (t == NULL)
|
|
break;
|
|
}
|
|
|
|
if (t != NULL) for (i = 0; i <= max_d; i++)
|
|
{
|
|
if (hash[i] != NULL)
|
|
{
|
|
png_dsortp p;
|
|
|
|
for (p = hash[i]; p != NULL; p = p->next)
|
|
{
|
|
if (index_to_palette[p->left] < num_new_palette &&
|
|
index_to_palette[p->right] < num_new_palette)
|
|
{
|
|
int j, next_j;
|
|
|
|
if (num_new_palette & 0x01)
|
|
{
|
|
j = p->left;
|
|
next_j = p->right;
|
|
}
|
|
else
|
|
{
|
|
j = p->right;
|
|
next_j = p->left;
|
|
}
|
|
|
|
num_new_palette--;
|
|
/* NOTE: overwrites palette */
|
|
palette[index_to_palette[j]] =
|
|
palette[num_new_palette];
|
|
|
|
if (full_quantize == 0)
|
|
{
|
|
int k;
|
|
|
|
for (k = 0; k < num_palette; k++)
|
|
{
|
|
if (tr->map[k] == index_to_palette[j])
|
|
{
|
|
tr->map[k] = index_to_palette[next_j];
|
|
map_changed = 1;
|
|
}
|
|
|
|
if (tr->map[k] == num_new_palette)
|
|
{
|
|
tr->map[k] = index_to_palette[j];
|
|
map_changed = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
index_to_palette[palette_to_index[num_new_palette]] =
|
|
index_to_palette[j];
|
|
|
|
palette_to_index[index_to_palette[j]] =
|
|
palette_to_index[num_new_palette];
|
|
|
|
index_to_palette[j] =
|
|
png_check_byte(png_ptr, num_new_palette);
|
|
|
|
palette_to_index[num_new_palette] =
|
|
png_check_byte(png_ptr, j);
|
|
}
|
|
|
|
if (num_new_palette <= maximum_colors)
|
|
break;
|
|
}
|
|
|
|
if (num_new_palette <= maximum_colors)
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 769; i++)
|
|
{
|
|
if (hash[i] != NULL)
|
|
{
|
|
png_dsortp p = hash[i];
|
|
|
|
while (p)
|
|
{
|
|
t = p->next;
|
|
png_free(png_ptr, p);
|
|
p = t;
|
|
}
|
|
|
|
hash[i] = NULL;
|
|
}
|
|
}
|
|
|
|
max_d += 96;
|
|
} /* while num_new_colors > maximum_colors */
|
|
} /* no histogram */
|
|
|
|
num_palette = maximum_colors;
|
|
|
|
if (map_changed) /* else the map is 1:1 */
|
|
tr->tr.args |= PNG_QUANTIZE_MAP;
|
|
} /* num_palette > maximum_colors */
|
|
|
|
/* The palette has been reduced to the requested number of colors if it
|
|
* was over maximum colors before.
|
|
*/
|
|
|
|
/* TODO: what is this? Apparently the png_struct::palette member gets
|
|
* updated if it didn't originally have a palette, but the update relies
|
|
* on the app not freeing the passed in palette.
|
|
*/
|
|
if (png_ptr->palette == NULL)
|
|
png_ptr->palette = palette;
|
|
|
|
png_ptr->num_palette = png_check_bits(png_ptr, num_palette, 9);
|
|
|
|
if (full_quantize)
|
|
{
|
|
int i;
|
|
png_byte distance[1U << (PNG_QUANTIZE_RED_BITS+PNG_QUANTIZE_GREEN_BITS+
|
|
PNG_QUANTIZE_BLUE_BITS)];
|
|
|
|
memset(distance, 0xff, sizeof distance);
|
|
|
|
for (i = 0; i < num_palette; i++)
|
|
{
|
|
int ir;
|
|
int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS));
|
|
int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS));
|
|
int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS));
|
|
|
|
for (ir = 0; ir < (1<<PNG_QUANTIZE_RED_BITS); ir++)
|
|
{
|
|
/* int dr = abs(ir - r); */
|
|
int ig;
|
|
int dr = ((ir > r) ? ir - r : r - ir);
|
|
int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS +
|
|
PNG_QUANTIZE_GREEN_BITS));
|
|
|
|
for (ig = 0; ig < (1<<PNG_QUANTIZE_GREEN_BITS); ig++)
|
|
{
|
|
/* int dg = abs(ig - g); */
|
|
int ib;
|
|
int dg = ((ig > g) ? ig - g : g - ig);
|
|
int dt = dr + dg;
|
|
int dm = ((dr > dg) ? dr : dg);
|
|
int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS);
|
|
|
|
for (ib = 0; ib < (1<<PNG_QUANTIZE_BLUE_BITS); ib++)
|
|
{
|
|
int d_index = index_g | ib;
|
|
/* int db = abs(ib - b); */
|
|
int db = ((ib > b) ? ib - b : b - ib);
|
|
int dmax = ((dm > db) ? dm : db);
|
|
int d = dmax + dt + db;
|
|
|
|
if (d < distance[d_index])
|
|
{
|
|
distance[d_index] = png_check_byte(png_ptr, d);
|
|
tr->lut[d_index] = png_check_byte(png_ptr, i);
|
|
}
|
|
} /* for blue */
|
|
} /* for green */
|
|
} /* for red */
|
|
} /* num_palette */
|
|
} /* full_quantize */
|
|
} /* png_ptr != NULL */
|
|
}
|
|
#endif /* READ_QUANTIZE */
|
|
|
|
#ifdef PNG_READ_PACK_SUPPORTED
|
|
/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel,
|
|
* without changing the actual values. Thus, if you had a row with
|
|
* a bit depth of 1, you would end up with bytes that only contained
|
|
* the numbers 0 or 1. If you would rather they contain 0 and 255, use
|
|
* png_set_expand_gray_1_2_4_to_8 instead.
|
|
*/
|
|
static void
|
|
png_do_read_unpack(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_const_bytep ep = png_voidcast(png_const_bytep, tc->dp);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
|
|
sp += PNG_TC_ROWBYTES(*tc) - 1; /* Start from end */
|
|
dp += tc->width; /* output bit depth is 8 */
|
|
|
|
# define png_ptr (tc->png_ptr)
|
|
png_debug(1, "in png_do_unpack");
|
|
|
|
switch (tc->bit_depth)
|
|
{
|
|
case 1:
|
|
{
|
|
/* Because we copy from the last pixel down the shift required
|
|
* at the start is 8-pixels_in_last_byte, which is just:
|
|
*/
|
|
unsigned int shift = 7U & -tc->width;
|
|
|
|
while (dp > ep)
|
|
{
|
|
*--dp = (*sp >> shift) & 1U;
|
|
shift = 7U & (shift+1U);
|
|
if (shift == 0U)
|
|
--sp;
|
|
}
|
|
|
|
debug(shift == 0U);
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
unsigned int shift = 7U & -(tc->width << 1);
|
|
|
|
while (dp > ep)
|
|
{
|
|
*--dp = (*sp >> shift) & 3U;
|
|
shift = 7U & (shift+2U);
|
|
if (shift == 0U)
|
|
--sp;
|
|
}
|
|
|
|
debug(shift == 0U);
|
|
break;
|
|
}
|
|
|
|
case 4:
|
|
{
|
|
unsigned int shift = 7U & -(tc->width << 2);
|
|
|
|
while (dp > ep)
|
|
{
|
|
*--dp = (*sp >> shift) & 15U;
|
|
shift = 7U & (shift+4U);
|
|
if (shift == 0U)
|
|
--sp;
|
|
}
|
|
|
|
debug(shift == 0U);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
impossible("bit depth");
|
|
}
|
|
|
|
debug(dp == ep && sp == png_upcast(png_const_bytep, tc->sp)-1U);
|
|
tc->sp = dp;
|
|
|
|
if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U)
|
|
{
|
|
tc->range++;
|
|
tc->format |= PNG_FORMAT_FLAG_RANGE;
|
|
}
|
|
|
|
tc->bit_depth = 8U;
|
|
PNG_UNUSED(transform)
|
|
# undef png_ptr
|
|
}
|
|
|
|
/* Called from the curiously named png_set_packing API in pngtrans.c; the read
|
|
* and write code is separated because read 'unpacks' (from PNG format) and
|
|
* write 'packs' (to PNG format.)
|
|
*/
|
|
void /* PRIVATE */
|
|
png_init_read_pack(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr tc->png_ptr
|
|
debug(tc->init);
|
|
|
|
if (tc->bit_depth < 8) /* else no packing/unpacking */
|
|
{
|
|
/* For indexed images the pack operation does not invalidate the range; in
|
|
* fact the corresponding shift operation would!
|
|
*/
|
|
if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U)
|
|
{
|
|
tc->range++;
|
|
tc->format |= PNG_FORMAT_FLAG_RANGE;
|
|
}
|
|
|
|
tc->bit_depth = 8U;
|
|
|
|
if (tc->init == PNG_TC_INIT_FINAL)
|
|
(*transform)->fn = png_do_read_unpack/* sic: it unpacks */;
|
|
}
|
|
|
|
else /* the transform is not applicable */
|
|
(*transform)->fn = NULL;
|
|
|
|
# undef png_ptr
|
|
}
|
|
#endif /* READ_PACK */
|
|
|
|
#if defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
static unsigned int
|
|
fill_transparent_pixel(png_const_structrp png_ptr, png_byte *trans)
|
|
/* Fill a byte array according to the transparent pixel value and return a
|
|
* count of the number of bytes. Low bit depth gray values are replicated in
|
|
* the first byte. Writes from 1 to 6 bytes.
|
|
*/
|
|
{
|
|
/* There must be a tRNS chunk and this must not be a palette image: */
|
|
debug(png_ptr->num_trans == 1 &&
|
|
!(png_ptr->color_type & (PNG_COLOR_MASK_ALPHA+PNG_COLOR_MASK_PALETTE)));
|
|
|
|
if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* gray */
|
|
{
|
|
unsigned int t = png_ptr->trans_color.gray;
|
|
unsigned int depth = png_ptr->bit_depth;
|
|
|
|
if (depth < 16U)
|
|
{
|
|
/* ISO PNG 11.3.2.1 "tRNS Transparency": "If the image bit depth is
|
|
* less than 16, the least significant bits are used and the others are
|
|
* 0." So mask out the upper bits.
|
|
*/
|
|
t &= (1U<<depth)-1U;
|
|
|
|
/* And replicate low bit-depth values across the byte: */
|
|
while (depth < 8U)
|
|
{
|
|
t |= t << depth;
|
|
depth <<= 1;
|
|
}
|
|
|
|
trans[0] = PNG_BYTE(t);
|
|
return 1U;
|
|
}
|
|
|
|
/* Else a 16 bit value: */
|
|
trans[0] = PNG_BYTE(t >> 8);
|
|
trans[1] = PNG_BYTE(t);
|
|
return 2U;
|
|
}
|
|
|
|
else /* color */ switch (png_ptr->bit_depth)
|
|
{
|
|
case 8: /* 8-bit RGB */
|
|
trans[0] = PNG_BYTE(png_ptr->trans_color.red);
|
|
trans[1] = PNG_BYTE(png_ptr->trans_color.green);
|
|
trans[2] = PNG_BYTE(png_ptr->trans_color.blue);
|
|
return 3U;
|
|
|
|
case 16: /* 16-bit RGB */
|
|
trans[0] = PNG_BYTE(png_ptr->trans_color.red >> 8);
|
|
trans[1] = PNG_BYTE(png_ptr->trans_color.red);
|
|
trans[2] = PNG_BYTE(png_ptr->trans_color.green >> 8);
|
|
trans[3] = PNG_BYTE(png_ptr->trans_color.green);
|
|
trans[4] = PNG_BYTE(png_ptr->trans_color.blue >> 8);
|
|
trans[5] = PNG_BYTE(png_ptr->trans_color.blue);
|
|
return 6U;
|
|
|
|
default:
|
|
NOT_REACHED;
|
|
return 0U; /* safe */
|
|
}
|
|
}
|
|
# endif /* READ_tRNS */
|
|
#endif /* READ_EXPAND || READ_BACKGROUND */
|
|
|
|
#ifdef PNG_READ_EXPAND_SUPPORTED
|
|
/* Flags for png_init_expand */
|
|
#define PNG_EXPAND_PALETTE 1U /* palette images only, includes tRNS */
|
|
#define PNG_EXPAND_LBD_GRAY 2U /* grayscale low-bit depth only */
|
|
#define PNG_EXPAND_tRNS 4U /* non-palette images only */
|
|
|
|
/* This struct is only required for tRNS matching, but it is convenient to
|
|
* allocated it anyway even if READ_tRNS is not supported.
|
|
*/
|
|
typedef struct
|
|
{
|
|
png_transform tr;
|
|
unsigned int ntrans; /* number of bytes below */
|
|
png_byte transparent_pixel[6]; /* the transparent pixel value */
|
|
} png_expand;
|
|
|
|
#ifdef PNG_READ_tRNS_SUPPORTED
|
|
/* Look for colors matching the trans_color in png_ptr, low bit depth gray is
|
|
* covered below so this only need handle 8 abd 16-bit channels.
|
|
*/
|
|
static void
|
|
png_do_expand_tRNS(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_expand *tr = png_transform_cast(png_expand, *transform);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp;
|
|
const unsigned int spixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
|
|
unsigned int alpha_size;
|
|
|
|
/* We expect opaque and transparent pixels to be interleaved but with long
|
|
* sequences of each. Because we are adding an alpha channel we must copy
|
|
* down.
|
|
*/
|
|
debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA));
|
|
debug(spixel_size == tr->ntrans);
|
|
sp += PNG_TC_ROWBYTES(*tc);
|
|
tc->sp = dp;
|
|
tc->format |= PNG_FORMAT_FLAG_ALPHA;
|
|
tc->invalid_info |= PNG_INFO_tRNS;
|
|
tc->transparent_alpha = 1U;
|
|
alpha_size = (PNG_TC_PIXEL_DEPTH(*tc)>>3) - spixel_size;
|
|
debug(alpha_size == 1 || alpha_size == 2);
|
|
dp += PNG_TC_ROWBYTES(*tc);
|
|
|
|
do
|
|
{
|
|
unsigned int i = spixel_size;
|
|
png_byte alpha = 0U;
|
|
|
|
dp -= alpha_size;
|
|
alpha = 0U;
|
|
|
|
/* Copy and check one source pixel (backwards, to avoid any
|
|
* overwrite):
|
|
*/
|
|
do if ((*--dp = *--sp) != tr->transparent_pixel[--i]) /* pixel != tRNS */
|
|
alpha = 0xFFU;
|
|
while (i != 0U);
|
|
|
|
/* i == 0 */
|
|
do
|
|
dp[spixel_size + i] = alpha;
|
|
while (++i < alpha_size);
|
|
} while (sp > ep);
|
|
|
|
debug(sp == ep && dp == tc->dp); /* else overwrite */
|
|
# undef png_ptr
|
|
}
|
|
#endif /* READ_tRNS */
|
|
|
|
/* Expand grayscale images of less than 8-bit depth to 8 bits.
|
|
* libpng 1.7.0: this no longer expands everything, it just expands the low bit
|
|
* depth gray row. It does *NOT* expand the tRNS into an alpha channel unless
|
|
* it is told to do so.
|
|
*
|
|
* API CHANGE: the function now does what it was always meant to do.
|
|
*
|
|
* This is like do_unpack except that the packed data is expanded to the full
|
|
* 8-bit range; scaled up. This is not a good thing to do on an indexed image;
|
|
* the indices will be invalid.
|
|
*
|
|
* The tRNS handling is included here too; speed is not important because the
|
|
* result will always be cached unless the PNG is very small.
|
|
*/
|
|
static void
|
|
png_do_expand_lbd_gray(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
const png_const_bytep ep = dp;
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const unsigned int bit_depth = tc->bit_depth;
|
|
# ifdef PNG_READ_sBIT_SUPPORTED
|
|
unsigned int insignificant_bits = 0U;
|
|
# endif /* READ_sBIT */
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
unsigned int gray = 0xffffU; /* doesn't match anything */
|
|
unsigned int do_alpha = 0U;
|
|
# endif /* READ_tRNS */
|
|
|
|
sp += PNG_TC_ROWBYTES(*tc); /* last byte +1 */
|
|
tc->bit_depth = 8U;
|
|
tc->invalid_info |= PNG_INFO_tRNS;
|
|
# ifdef PNG_READ_sBIT_SUPPORTED
|
|
if (bit_depth > 1U /* irrelevant for bit depth 1 */ &&
|
|
!(tc->invalid_info & PNG_INFO_sBIT) &&
|
|
tc->sBIT_G > 0U/*SAFETY*/ && tc->sBIT_G < bit_depth)
|
|
insignificant_bits = bit_depth - tc->sBIT_G;
|
|
# endif /* READ_sBIT */
|
|
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
if (((*transform)->args & PNG_EXPAND_tRNS) != 0)
|
|
{
|
|
tc->format |= PNG_FORMAT_FLAG_ALPHA;
|
|
tc->transparent_alpha = 1U;
|
|
gray = png_ptr->trans_color.gray & ((1U << bit_depth)-1U);
|
|
do_alpha = 1U;
|
|
}
|
|
|
|
/* This helps avoid cluttering the code up with #ifdefs: */
|
|
# define check_tRNS if (do_alpha) *--dp = (pixel != gray) * 255U;
|
|
# else /* !READ_tRNS */
|
|
# define check_tRNS
|
|
# endif /* READ_tRNS */
|
|
|
|
dp += PNG_TC_ROWBYTES(*tc); /* pre-decremented below */
|
|
|
|
switch (bit_depth)
|
|
{
|
|
case 1:
|
|
{
|
|
unsigned int shift = 7U & -tc->width;
|
|
unsigned int s = *--sp;
|
|
|
|
for(;;)
|
|
{
|
|
if (shift == 8U) s = *--sp, shift = 0;
|
|
|
|
{
|
|
const unsigned int pixel = (s >> shift) & 1U;
|
|
|
|
check_tRNS
|
|
*--dp = PNG_BYTE(pixel * 255U);
|
|
if (dp <= ep) break;
|
|
}
|
|
++shift;
|
|
}
|
|
|
|
debug(dp == ep && shift == 7U && sp == tc->sp);
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
unsigned int shift = 7U & -(tc->width << 1)/*overflow ok*/;
|
|
unsigned int s = *--sp;
|
|
|
|
for (;;)
|
|
{
|
|
if (shift == 8U) s = *--sp, shift = 0;
|
|
{
|
|
const unsigned int pixel = (s >> shift) & 3U;
|
|
|
|
check_tRNS
|
|
|
|
# ifdef PNG_READ_sBIT_SUPPORTED
|
|
/* 'sig_bits' must be 1 or 2 leaving insignificant_bits 0 or
|
|
* 1. This may look silly but it allows a compact representation
|
|
* of 1 bit gray + 1 bit alpha (transparency):
|
|
*/
|
|
if (insignificant_bits /* only 1 bit significant */)
|
|
*--dp = PNG_BYTE((pixel >> 1) * 255U);
|
|
|
|
else
|
|
# endif
|
|
*--dp = PNG_BYTE(pixel * 85U);
|
|
|
|
if (dp <= ep) break;
|
|
}
|
|
shift += 2U;
|
|
}
|
|
|
|
debug(dp == ep && shift == 6U && sp == tc->sp);
|
|
break;
|
|
}
|
|
|
|
case 4:
|
|
{
|
|
unsigned int shift = 7U & -(tc->width << 2)/*overflow ok*/;
|
|
unsigned int s = *--sp;
|
|
# ifdef PNG_READ_sBIT_SUPPORTED
|
|
const unsigned int div = (1U << (4U-insignificant_bits)) - 1U;
|
|
# endif
|
|
|
|
for (;;)
|
|
{
|
|
if (shift == 8U) s = *--sp, shift = 0;
|
|
{
|
|
unsigned int pixel = (s >> shift) & 15U;
|
|
|
|
check_tRNS
|
|
|
|
# ifdef PNG_READ_sBIT_SUPPORTED
|
|
/* insignificant_bits may be 0, 1, 2 or 3, requiring a multiply
|
|
* by 17, 255/7, 85 or 255. Since this operation is always
|
|
* cached we don't much care about the time to do the divide
|
|
* below.
|
|
*/
|
|
if (insignificant_bits)
|
|
pixel = ((pixel>>insignificant_bits) * 255U + (div>>1)) / div;
|
|
|
|
else
|
|
# endif
|
|
pixel *= 17U;
|
|
|
|
*--dp = PNG_BYTE(pixel);
|
|
if (dp <= ep) break;
|
|
}
|
|
|
|
shift += 4U;
|
|
}
|
|
|
|
debug(dp == ep && shift == 4U && sp == tc->sp);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
impossible("bit depth");
|
|
}
|
|
|
|
tc->sp = ep;
|
|
|
|
# undef check_tRNS
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_init_expand(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
/* The possible combinations are:
|
|
*
|
|
* 1) PALETTE: the 'palette' flag should be set on the transform control and
|
|
* all that need be done is cancel this to cause the cache code to do the
|
|
* expansion.
|
|
*
|
|
* 2) LBP_GRAY, LBP_GRAY+tRNS: use png_do_expand_lbd_gray to do the required
|
|
* expand. Can be cached.
|
|
*
|
|
* 3) tRNS: scan the row for the relevant tRNS value.
|
|
*
|
|
* Note that expanding 8 to 16 bits is a byte op in pngtrans.c (it just
|
|
* replicates bytes).
|
|
*/
|
|
if (tc->palette)
|
|
{
|
|
debug(tc->caching && !(tc->format & PNG_FORMAT_FLAG_COLORMAP));
|
|
|
|
if (((*transform)->args & PNG_EXPAND_PALETTE) != 0U)
|
|
{
|
|
tc->palette = 0U;
|
|
tc->invalid_info |= PNG_INFO_PLTE + PNG_INFO_tRNS;
|
|
tc->cost = PNG_CACHE_COST_LIMIT; /* the cache is required! */
|
|
}
|
|
|
|
/* Note that this needs to happen when the row is processed (!tc->init) as
|
|
* well.
|
|
*/
|
|
}
|
|
|
|
else if (!(tc->format & PNG_FORMAT_FLAG_COLORMAP))
|
|
{
|
|
png_uint_32 args = (*transform)->args & PNG_BIC_MASK(PNG_EXPAND_PALETTE);
|
|
unsigned int bit_depth = tc->bit_depth;
|
|
|
|
debug(tc->init);
|
|
|
|
if (bit_depth >= 8U)
|
|
args &= PNG_BIC_MASK(PNG_EXPAND_LBD_GRAY);
|
|
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
if (png_ptr->num_trans == 0U ||
|
|
(tc->format & PNG_FORMAT_FLAG_ALPHA) != 0U ||
|
|
(tc->invalid_info & PNG_INFO_tRNS) != 0U)
|
|
# endif
|
|
args &= PNG_BIC_MASK(PNG_EXPAND_tRNS);
|
|
|
|
(*transform)->args = args;
|
|
|
|
switch (args)
|
|
{
|
|
case PNG_EXPAND_LBD_GRAY:
|
|
tc->bit_depth = 8U;
|
|
tc->invalid_info |= PNG_INFO_tRNS;
|
|
|
|
if (tc->init == PNG_TC_INIT_FINAL)
|
|
(*transform)->fn = png_do_expand_lbd_gray;
|
|
break;
|
|
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
case PNG_EXPAND_LBD_GRAY + PNG_EXPAND_tRNS:
|
|
tc->bit_depth = 8U;
|
|
tc->format |= PNG_FORMAT_FLAG_ALPHA;
|
|
tc->invalid_info |= PNG_INFO_tRNS;
|
|
tc->transparent_alpha = 1U;
|
|
|
|
/* In this case tRNS must be left unmodified for the expansion code
|
|
* to handle.
|
|
*/
|
|
if (tc->init == PNG_TC_INIT_FINAL)
|
|
(*transform)->fn = png_do_expand_lbd_gray;
|
|
break;
|
|
|
|
case PNG_EXPAND_tRNS:
|
|
if (tc->init == PNG_TC_INIT_FINAL)
|
|
{
|
|
png_expand *tr = png_transform_cast(png_expand, *transform);
|
|
|
|
affirm((tc->bit_depth == 8U || tc->bit_depth == 16U) &&
|
|
(tc->format &
|
|
(PNG_FORMAT_FLAG_COLORMAP|PNG_FORMAT_FLAG_ALPHA)) == 0U);
|
|
|
|
tr->ntrans = fill_transparent_pixel(png_ptr,
|
|
tr->transparent_pixel);
|
|
tr->tr.fn = png_do_expand_tRNS;
|
|
} /* TC_INIT_FINAL */
|
|
|
|
tc->format |= PNG_FORMAT_FLAG_ALPHA;
|
|
tc->invalid_info |= PNG_INFO_tRNS;
|
|
tc->transparent_alpha = 1U;
|
|
break;
|
|
# endif /* READ_tRNS */
|
|
|
|
default: /* transform not applicable */
|
|
(*transform)->fn = NULL;
|
|
break;
|
|
}
|
|
|
|
implies(tc->init == PNG_TC_INIT_FINAL,
|
|
(*transform)->fn != png_init_expand);
|
|
}
|
|
|
|
else /* not applicable */
|
|
{
|
|
debug(tc->init);
|
|
(*transform)->fn = NULL;
|
|
NOT_REACHED;
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
void PNGAPI
|
|
png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr)
|
|
{
|
|
if (png_ptr != NULL)
|
|
png_add_transform(png_ptr, sizeof (png_expand), png_init_expand,
|
|
PNG_TR_EXPAND)->args |= PNG_EXPAND_LBD_GRAY;
|
|
}
|
|
|
|
/* Expand paletted images to 8-bit RGB or, if there is a tRNS chunk, RGBA.
|
|
* Note that this is effectively handled by the read code palette optimizations.
|
|
*
|
|
* API CHANGE: this used to have the completely unexpected side effect of
|
|
* turning on the above two optimizations.
|
|
*/
|
|
void PNGAPI
|
|
png_set_palette_to_rgb(png_structrp png_ptr)
|
|
{
|
|
if (png_ptr != NULL)
|
|
png_add_transform(png_ptr, sizeof (png_expand), png_init_expand,
|
|
PNG_TR_EXPAND)->args |= PNG_EXPAND_PALETTE;
|
|
}
|
|
|
|
/* Expand paletted images to RGB, expand grayscale images of less than 8-bit
|
|
* depth to 8-bit depth, and expand tRNS chunks to alpha channels. I.e. all the
|
|
* above.
|
|
*/
|
|
void PNGAPI
|
|
png_set_expand(png_structrp png_ptr)
|
|
{
|
|
if (png_ptr != NULL)
|
|
{
|
|
png_set_palette_to_rgb(png_ptr);
|
|
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
|
png_set_tRNS_to_alpha(png_ptr);
|
|
}
|
|
}
|
|
#endif /* READ_EXPAND */
|
|
|
|
#if defined(PNG_READ_EXPAND_SUPPORTED) ||\
|
|
defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
|
|
|
|
#define PNG_INIT_STRIP_ALPHA 1U
|
|
#define PNG_INIT_EXPAND_tRNS 2U
|
|
static void
|
|
png_init_alpha(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
int required = 0;
|
|
|
|
# if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_tRNS_SUPPORTED)
|
|
if ((*transform)->args & PNG_INIT_EXPAND_tRNS)
|
|
{
|
|
/* Prior to 1.7 the alpha channel was stripped after expanding the tRNS
|
|
* chunk, so this effectively cancelled out the expand.
|
|
*/
|
|
if (png_ptr->num_trans > 0 && !tc->palette &&
|
|
!((*transform)->args & PNG_INIT_STRIP_ALPHA))
|
|
{
|
|
debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0);
|
|
|
|
required = 1;
|
|
tc->expand_tRNS = 1U;
|
|
|
|
/* This happens as a result of an explicit API call to
|
|
* png_set_tRNS_to_alpha, so expand low-bit-depth gray too:
|
|
*/
|
|
if (tc->init == PNG_TC_INIT_FORMAT)
|
|
png_add_transform(png_ptr, sizeof (png_expand), png_init_expand,
|
|
PNG_TR_EXPAND)->args |= PNG_EXPAND_tRNS + PNG_EXPAND_LBD_GRAY;
|
|
}
|
|
|
|
else
|
|
(*transform)->args &= PNG_BIC_MASK(PNG_INIT_EXPAND_tRNS);
|
|
}
|
|
# endif /* READ_EXPAND && READ_tRNS */
|
|
|
|
# ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
|
|
if ((*transform)->args & PNG_INIT_STRIP_ALPHA)
|
|
{
|
|
/* When compose is being done tRNS will be expanded regardless of the
|
|
* above test. Rather that trying to work out if this will happen the
|
|
* code just inserts a strip operation; it will be removed later if it
|
|
* is not needed.
|
|
*/
|
|
required = 1;
|
|
tc->strip_alpha = 1U;
|
|
|
|
if (tc->init == PNG_TC_INIT_FORMAT)
|
|
png_add_strip_alpha_byte_ops(png_ptr);
|
|
}
|
|
# endif /* READ_STRIP_ALPHA */
|
|
|
|
if (!required)
|
|
(*transform)->fn = NULL;
|
|
# undef png_ptr
|
|
}
|
|
#endif /* READ_EXPAND || READ_STRIP_ALPHA */
|
|
|
|
#ifdef PNG_READ_EXPAND_SUPPORTED
|
|
/* Expand tRNS chunks to alpha channels. This only expands the tRNS chunk on
|
|
* non-palette formats; call png_set_palette_to_rgb to get the corresponding
|
|
* effect for a palette.
|
|
*
|
|
* Note that this will expand low bit depth gray if there is a tRNS chunk, but
|
|
* if not nothing will happen.
|
|
*
|
|
* API CHANGE: this used to do all the expansions, it was rather pointless
|
|
* calling it.
|
|
*/
|
|
void PNGAPI
|
|
png_set_tRNS_to_alpha(png_structrp png_ptr)
|
|
{
|
|
if (png_ptr != NULL)
|
|
png_add_transform(png_ptr, 0/*size*/, png_init_alpha, PNG_TR_INIT_ALPHA)->
|
|
args |= PNG_INIT_EXPAND_tRNS;
|
|
}
|
|
#endif /* READ_EXPAND */
|
|
|
|
#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
|
|
void PNGAPI
|
|
png_set_strip_alpha(png_structrp png_ptr)
|
|
{
|
|
if (png_ptr != NULL)
|
|
png_add_transform(png_ptr, 0/*size*/, png_init_alpha, PNG_TR_INIT_ALPHA)->
|
|
args |= PNG_INIT_STRIP_ALPHA;
|
|
}
|
|
#endif /* READ_STRIP_ALPHA */
|
|
|
|
#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
|
|
static void
|
|
png_do_chop_16_to_8(png_transformp *transform, png_transform_controlp tc)
|
|
/* This is actually a repeat of the byte transform, unnecessary code
|
|
* replication.
|
|
*
|
|
* TODO: remove this
|
|
*/
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); /* source */
|
|
png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); /* end+1 */
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp); /* destination */
|
|
|
|
debug(tc->bit_depth == 16U);
|
|
tc->sp = dp;
|
|
tc->bit_depth = 8U;
|
|
|
|
while (sp < ep)
|
|
*dp++ = *sp, sp += 2;
|
|
|
|
debug(sp == ep);
|
|
# undef png_ptr
|
|
|
|
PNG_UNUSED(transform)
|
|
}
|
|
|
|
/* A transform containing some useful scaling values... */
|
|
typedef struct
|
|
{
|
|
png_transform tr;
|
|
png_uint_32 shifts; /* 4 4-bit values preceeded by a shibboleth (1) */
|
|
png_uint_32 channel_scale[4];
|
|
} png_transform_scale_16_to_8;
|
|
|
|
/* Scale rows of bit depth 16 down to 8 accurately */
|
|
static void
|
|
png_do_scale_16_to_8(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); /* source */
|
|
png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc); /* end+1 */
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp); /* destination */
|
|
png_transform_scale_16_to_8 *tr =
|
|
png_transform_cast(png_transform_scale_16_to_8, *transform);
|
|
png_uint_32p scale = 0;
|
|
png_uint_32 shift = 1U; /* set the shibboleth at the start */
|
|
|
|
debug(tc->bit_depth == 16U);
|
|
tc->sp = dp;
|
|
tc->bit_depth = 8U;
|
|
|
|
while (sp < ep)
|
|
{
|
|
/* The input is an array of 16 bit components, these must be scaled to
|
|
* 8 bits each taking into account the sBIT setting. The calculation
|
|
* requires that the insignificant bits be stripped from the input value
|
|
* via a shift then scaled back to 8 bits:
|
|
*
|
|
* output = ((input >> shift) * scale + round) >> 24
|
|
*
|
|
* The shifts are packed into tr->shifts, with the end of the list marked
|
|
* by a shibboleth, 1, which is preset above.
|
|
*/
|
|
png_uint_32 v = png_get_uint_16(sp);
|
|
|
|
sp += 2;
|
|
|
|
if (shift == 1U)
|
|
{
|
|
shift = tr->shifts;
|
|
scale = tr->channel_scale;
|
|
}
|
|
|
|
*dp++ = PNG_BYTE(((v >> (shift & 0xFU)) * *scale++ + 0x800000U) >> 24);
|
|
shift >>= 4;
|
|
}
|
|
|
|
affirm(sp == ep);
|
|
# undef png_ptr
|
|
}
|
|
|
|
static int
|
|
add_scale(png_transform_scale_16_to_8 *tr, unsigned int sBIT, unsigned int ch)
|
|
{
|
|
/* This is the output max (255) scaled by 2^24 divided by the input max'
|
|
* (which is variable) and rounded. It gives the exact 8-bit answer for all
|
|
* input sBIT depths when used in the calculation:
|
|
*
|
|
* output = ((input >> shift) * scale + 0x800000U) >> 24
|
|
*/
|
|
tr->channel_scale[ch] = (0xFF000000U + ((1U<<sBIT)>>1)) / ((1U<<sBIT)-1U);
|
|
tr->shifts |= ((16U-sBIT) & 0xFU) << (4U*ch);
|
|
|
|
/* The result says whether there are 8 or fewer significant bits in the
|
|
* input value; if so we can just drop the low byte.
|
|
*/
|
|
return sBIT <= 8U;
|
|
}
|
|
|
|
static void
|
|
png_init_scale_16_to_8(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
if (tc->bit_depth == 16U)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
tc->bit_depth = 8U;
|
|
/* But this invalidates tRNS (a 16-bit tRNS cannot be updated to match
|
|
* 8-bit data correctly).
|
|
*/
|
|
tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL;
|
|
/* TODO: These need further processing: PNG_INFO_bKGD */
|
|
|
|
if (tc->init == PNG_TC_INIT_FINAL)
|
|
{
|
|
png_transform_scale_16_to_8 *tr =
|
|
png_transform_cast(png_transform_scale_16_to_8, *transform);
|
|
|
|
/* Set the scale factors for each channel (up to 4), the factors are
|
|
* made so that:
|
|
*
|
|
* ((channel >> shift) * factor + 0x800000U) >> 24
|
|
*
|
|
* Gives the required 8-bit value. The 'shift' is stored in a single
|
|
* png_uint_32 with a shibboleth at the end.
|
|
*/
|
|
unsigned int channels = 0U;
|
|
int chop_ok = 1;
|
|
|
|
tr->shifts = 0U;
|
|
|
|
/* This adds up to four scale factors, the remainder are left as 0
|
|
* which is safe and leads to obvious errors in the output images in
|
|
* the event of an (internal) error.
|
|
*/
|
|
if (tc->format & PNG_FORMAT_FLAG_COLOR)
|
|
chop_ok &= add_scale(tr, tc->sBIT_R, channels++);
|
|
|
|
chop_ok &= add_scale(tr, tc->sBIT_G, channels++);
|
|
|
|
if (tc->format & PNG_FORMAT_FLAG_COLOR)
|
|
chop_ok &= add_scale(tr, tc->sBIT_B, channels++);
|
|
|
|
if (tc->format & PNG_FORMAT_FLAG_ALPHA)
|
|
chop_ok &= add_scale(tr, tc->sBIT_A, channels++);
|
|
|
|
if (chop_ok)
|
|
tr->tr.fn = png_do_chop_16_to_8;
|
|
|
|
else
|
|
{
|
|
int handled = 1;
|
|
|
|
/* Add the shibboleth at the end */
|
|
tr->shifts |= 1U << (4U*channels);
|
|
tr->tr.fn = png_do_scale_16_to_8;
|
|
|
|
/* sBIT is a little tricky; it has to be processed in the scaling
|
|
* operation. The result will have the same number of bits unless
|
|
* there were more than 8 before. The sBIT flags in the transform
|
|
* control are left unchanged here because the data is still valid,
|
|
* unless all the values end up as 8 in which case there is no
|
|
* remaining sBIT info.
|
|
*
|
|
* Note that fields, such as alpha, which are not set for this row
|
|
* format will always have max values, so won't reset 'handled':
|
|
*/
|
|
if (tc->sBIT_R >= 8U) tc->sBIT_R = 8U; else handled = 0;
|
|
if (tc->sBIT_G >= 8U) tc->sBIT_G = 8U; else handled = 0;
|
|
if (tc->sBIT_B >= 8U) tc->sBIT_B = 8U; else handled = 0;
|
|
if (tc->sBIT_A >= 8U) tc->sBIT_A = 8U; else handled = 0;
|
|
|
|
/* If all the sBIT values were >= 8U all the bits are now
|
|
* significant:
|
|
*/
|
|
if (handled)
|
|
tc->invalid_info |= PNG_INFO_sBIT;
|
|
}
|
|
}
|
|
|
|
# undef png_ptr
|
|
}
|
|
|
|
else /* not applicable */
|
|
(*transform)->fn = NULL;
|
|
}
|
|
|
|
void PNGAPI
|
|
png_set_scale_16(png_structrp png_ptr)
|
|
{
|
|
if (png_ptr != NULL)
|
|
png_add_transform(png_ptr, sizeof (png_transform_scale_16_to_8),
|
|
png_init_scale_16_to_8, PNG_TR_SCALE_16_TO_8);
|
|
}
|
|
#endif /* READ_SCALE_16_TO_8 */
|
|
|
|
#ifdef PNG_READ_GAMMA_SUPPORTED
|
|
/* Code that depends on READ_GAMMA support; RGB to gray convertion and
|
|
* background composition (including the various alpha-mode handling
|
|
* operations which produce pre-multiplied alpha by composing on 0).
|
|
*/
|
|
/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */
|
|
static png_fixed_point
|
|
png_reciprocal(png_fixed_point a)
|
|
{
|
|
#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
|
|
double r = floor(1E10/a+.5);
|
|
|
|
if (r <= 2147483647. && r >= -2147483648.)
|
|
return (png_fixed_point)r;
|
|
#else
|
|
png_fixed_point res;
|
|
|
|
if (png_muldiv(&res, PNG_FP_1, PNG_FP_1, a) != 0)
|
|
return res;
|
|
#endif
|
|
|
|
return 0; /* error/overflow */
|
|
}
|
|
|
|
/* This is the shared test on whether a gamma value is 'significant' - whether
|
|
* it is worth doing gamma correction. 'significant_bits' is the number of bits
|
|
* in the values to be corrected which are significant.
|
|
*/
|
|
static int
|
|
png_gamma_significant(png_const_structrp png_ptr, png_fixed_point gamma_val,
|
|
unsigned int sbits)
|
|
{
|
|
#if 0
|
|
/* This seems to be wrong. The issue is that when the app asks for a higher
|
|
* bit depth output than the input has significant bits it causes gamma
|
|
* correction to be skipped (this was the intent) however there's no
|
|
* particular guarantee that the app won't go on to do further gamma
|
|
* processing - pngstest does this - and this messes up the results
|
|
* completely.
|
|
*
|
|
* TODO: work out how to optimize this correctly.
|
|
*/
|
|
/* The following table lists the threshold as a difference from PNG_FP_1 at
|
|
* which the gamma correction will make a change to at least an 'sbits'
|
|
* value. There is no entry for 1 bit values; gamma correction is never
|
|
* significant.
|
|
*/
|
|
static const png_uint_16 gamma_threshold_by_sbit[15][2] =
|
|
{
|
|
{ 36907, 63092 }, /* 2 bits */
|
|
{ 17812, 21518 }, /* 3 bits */
|
|
{ 8675, 9496 }, /* 4 bits */
|
|
{ 4290, 4484 }, /* 5 bits */
|
|
{ 2134, 2181 }, /* 6 bits */
|
|
{ 1064, 1075 }, /* 7 bits */
|
|
{ 531, 534 }, /* 8 bits */
|
|
{ 265, 266 }, /* 9 bits */
|
|
{ 132, 132 }, /* 10 bits */
|
|
{ 66, 66 }, /* 11 bits */
|
|
{ 33, 33 }, /* 12 bits */
|
|
{ 16, 16 }, /* 13 bits */
|
|
{ 8, 8 }, /* 14 bits */
|
|
{ 4, 4 }, /* 15 bits */
|
|
{ 2, 2 }, /* 16 bits */
|
|
};
|
|
|
|
/* Handle out of range values in release by doing the gamma correction: */
|
|
debug_handled(sbits > 0U && sbits <= 16U);
|
|
if (sbits == 0U || sbits > 16U)
|
|
return 1;
|
|
|
|
/* 1 bit input or zero gamma, no correction possible/required: */
|
|
if (gamma_val == 0 || sbits < 2U)
|
|
return 0;
|
|
|
|
if (gamma_val < PNG_FP_1 - gamma_threshold_by_sbit[sbits-2U][0U])
|
|
return gamma_val < PNG_FP_1 - png_ptr->gamma_threshold;
|
|
|
|
else if (gamma_val > PNG_FP_1 + gamma_threshold_by_sbit[sbits-2U][1U])
|
|
return gamma_val > PNG_FP_1 + png_ptr->gamma_threshold;
|
|
#else /* FIXUP */
|
|
if (gamma_val < PNG_FP_1)
|
|
return gamma_val < PNG_FP_1 - png_ptr->gamma_threshold;
|
|
|
|
else if (gamma_val > PNG_FP_1)
|
|
return gamma_val > PNG_FP_1 + png_ptr->gamma_threshold;
|
|
|
|
PNG_UNUSED(sbits)
|
|
#endif /* FIXUP */
|
|
|
|
return 0; /* not significant */
|
|
}
|
|
|
|
static int
|
|
png_gamma_equal(png_const_structrp png_ptr, png_fixed_point g1,
|
|
png_fixed_point g2, png_fixed_point *c, unsigned int sbits)
|
|
/* Gamma values are equal, or at least one is unknown; c is the correction
|
|
* factor from g1 to g2, i.e. g2/g1.
|
|
*/
|
|
{
|
|
return sbits == 1U || g1 == 0 || g2 == 0 || g1 == g2 ||
|
|
(png_muldiv(c, g2, PNG_FP_1, g1) &&
|
|
!png_gamma_significant(png_ptr, *c, sbits));
|
|
}
|
|
|
|
#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
|
|
int
|
|
png_need_gamma_correction(png_const_structrp png_ptr, png_fixed_point gamma,
|
|
int sRGB_output)
|
|
/* This is a hook for the simplified code; it just decides whether or not the
|
|
* given gamma (which defaults to that of the PNG data) is close enough to
|
|
* linear or sRGB not to require gamma correction.
|
|
*/
|
|
{
|
|
if (gamma == 0)
|
|
gamma = png_ptr->colorspace.gamma;
|
|
|
|
if (gamma != 0 &&
|
|
(png_ptr->colorspace.flags &
|
|
(PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) ==
|
|
PNG_COLORSPACE_HAVE_GAMMA)
|
|
{
|
|
|
|
if (sRGB_output && !png_muldiv(&gamma, gamma, PNG_GAMMA_sRGB, PNG_FP_1))
|
|
return 0; /* overflow, so no correction */
|
|
|
|
return png_gamma_significant(png_ptr, gamma, (png_ptr->color_type &
|
|
PNG_COLOR_MASK_PALETTE) ? 8U : png_ptr->bit_depth);
|
|
}
|
|
|
|
return 0; /* no info, no correction */
|
|
}
|
|
#endif /* SIMPLIFIED_READ */
|
|
|
|
#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED
|
|
/* Fixed point gamma.
|
|
*
|
|
* The code to calculate the tables used below can be found in the shell script
|
|
* contrib/tools/intgamma.sh
|
|
*
|
|
* To calculate gamma this code implements fast log() and exp() calls using only
|
|
* fixed point arithmetic. This code has sufficient precision for either 8-bit
|
|
* or 16-bit sample values.
|
|
*
|
|
* The tables used here were calculated using simple 'bc' programs, but C double
|
|
* precision floating point arithmetic would work fine.
|
|
*
|
|
* 8-bit log table
|
|
* This is a table of -log(value/255)/log(2) for 'value' in the range 128 to
|
|
* 255, so it's the base 2 logarithm of a normalized 8-bit floating point
|
|
* mantissa. The numbers are 32-bit fractions.
|
|
*/
|
|
static const png_uint_32
|
|
png_8bit_l2[128] =
|
|
{
|
|
4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U,
|
|
3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U,
|
|
3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U,
|
|
3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U,
|
|
3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U,
|
|
2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U,
|
|
2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U,
|
|
2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U,
|
|
2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U,
|
|
2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U,
|
|
1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U,
|
|
1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U,
|
|
1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U,
|
|
1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U,
|
|
1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U,
|
|
971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U,
|
|
803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U,
|
|
639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U,
|
|
479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U,
|
|
324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U,
|
|
172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U,
|
|
24347096U, 0U
|
|
|
|
#if 0 /* NOT USED */
|
|
/* The following are the values for 16-bit tables - these work fine for the
|
|
* 8-bit conversions but produce very slightly larger errors in the 16-bit
|
|
* log (about 1.2 as opposed to 0.7 absolute error in the final value). To
|
|
* use these all the shifts below must be adjusted appropriately.
|
|
*/
|
|
65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054,
|
|
57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803,
|
|
50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068,
|
|
43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782,
|
|
37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887,
|
|
31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339,
|
|
25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098,
|
|
20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132,
|
|
15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415,
|
|
10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523,
|
|
6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495,
|
|
1119, 744, 372
|
|
#endif
|
|
};
|
|
|
|
#if 0 /* UNUSED */
|
|
static png_int_32
|
|
png_log8bit(unsigned int x)
|
|
{
|
|
png_uint_32 lg2 = 0U;
|
|
|
|
/* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log,
|
|
* because the log is actually negate that means adding 1. The final
|
|
* returned value thus has the range 0 (for 255 input) to 7.994 (for 1
|
|
* input), return -1 for the overflow (log 0) case, - so the result is
|
|
* always at most 19 bits.
|
|
*/
|
|
if ((x &= 0xffU) == 0U) /* 0 input, -inf output */
|
|
return -0xfffff;
|
|
|
|
if ((x & 0xf0U) == 0U)
|
|
lg2 = 4U, x <<= 4;
|
|
|
|
if ((x & 0xc0U) == 0U)
|
|
lg2 += 2U, x <<= 2;
|
|
|
|
if ((x & 0x80U) == 0U)
|
|
lg2 += 1U, x <<= 1;
|
|
|
|
/* result is at most 19 bits, so this cast is safe: */
|
|
return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128U]+32768U)>>16));
|
|
}
|
|
#endif /* UNUSED */
|
|
|
|
/* The above gives exact (to 16 binary places) log2 values for 8-bit images,
|
|
* for 16-bit images we use the most significant 8 bits of the 16-bit value to
|
|
* get an approximation then multiply the approximation by a correction factor
|
|
* determined by the remaining up to 8 bits. This requires an additional step
|
|
* in the 16-bit case.
|
|
*
|
|
* We want log2(value/65535), we have log2(v'/255), where:
|
|
*
|
|
* value = v' * 256 + v''
|
|
* = v' * f
|
|
*
|
|
* So f is value/v', which is equal to (256+v''/v') since v' is in the range 128
|
|
* to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less
|
|
* than 258. The final factor also needs to correct for the fact that our 8-bit
|
|
* value is scaled by 255, whereas the 16-bit values must be scaled by 65535.
|
|
*
|
|
* This gives a final formula using a calculated value 'x' which is value/v' and
|
|
* scaling by 65536 to match the above table:
|
|
*
|
|
* log2(x/257) * 65536
|
|
*
|
|
* Since these numbers are so close to '1' we can use simple linear
|
|
* interpolation between the two end values 256/257 (result -368.61) and 258/257
|
|
* (result 367.179). The values used below are scaled by a further 64 to give
|
|
* 16-bit precision in the interpolation:
|
|
*
|
|
* Start (256): -23591
|
|
* Zero (257): 0
|
|
* End (258): 23499
|
|
*
|
|
* In libpng 1.7.0 this is further generalized to return -log2(value/maxval) for
|
|
* any maxval up to 65535. This is done by evaluating -log2(value/65535) first
|
|
* then adjusting for the required maxval:
|
|
*
|
|
* ( value) (value 65535) (value) ( 65535)
|
|
* -log2(------) = -log2(----- x ------) = -log2(-----)-log2(------)
|
|
* (maxval) (65535 maxval) (65535) (maxval)
|
|
*
|
|
* The extra argument, 'factor', is (2^(16+12))*log2(65535/maxval) (a positive
|
|
* value less than 2^32) and this is *subtracted* from the intermediate
|
|
* calculation below.
|
|
*/
|
|
static png_int_32
|
|
png_log(unsigned int x, png_uint_32 factor)
|
|
/* x: a value of up to 16 bits,
|
|
* factor: a 4.28 number which is subtracted from the log below
|
|
*/
|
|
{
|
|
png_uint_32 lg2 = 0U;
|
|
|
|
/* As above, but now the input has 16 bits. */
|
|
if ((x &= 0xffffU) == 0U)
|
|
return -0xfffff;
|
|
|
|
if ((x & 0xff00U) == 0U)
|
|
lg2 = 8U, x <<= 8;
|
|
|
|
if ((x & 0xf000U) == 0U)
|
|
lg2 += 4U, x <<= 4;
|
|
|
|
if ((x & 0xc000U) == 0U)
|
|
lg2 += 2U, x <<= 2;
|
|
|
|
if ((x & 0x8000U) == 0U)
|
|
lg2 += 1U, x <<= 1;
|
|
|
|
/* Calculate the base logarithm from the top 8 bits as a 28-bit fractional
|
|
* value.
|
|
*/
|
|
lg2 <<= 28;
|
|
lg2 += (png_8bit_l2[(x>>8)-128U]+8U) >> 4;
|
|
|
|
/* Now we need to interpolate the factor, this requires a division by the top
|
|
* 8 bits. Do this with maximum precision.
|
|
*/
|
|
{
|
|
png_uint_32 i = x;
|
|
|
|
i = ((i << 16) + (i >> 9)) / (x>> 8);
|
|
|
|
/* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24,
|
|
* the value at 1<<16 (ignoring this) will be 0 or 1; this gives us
|
|
* exactly 16 bits to interpolate to get the low bits of the result.
|
|
* Round the answer. Note that the end point values are scaled by 64 to
|
|
* retain overall precision and that 'lg2' is current scaled by an extra
|
|
* 12 bits, so adjust the overall scaling by 6-12. Round at every step.
|
|
*/
|
|
i -= 1U << 24;
|
|
|
|
if (i <= 65536U) /* <= '257' */
|
|
lg2 += ((23591U * (65536U-i)) + (1U << (16+6-12-1))) >> (16+6-12);
|
|
|
|
else
|
|
lg2 -= ((23499U * (i-65536U)) + (1U << (16+6-12-1))) >> (16+6-12);
|
|
}
|
|
|
|
if (lg2 >= factor)
|
|
return (png_int_32)/*SAFE*/((lg2 - factor + 2048U) >> 12);
|
|
|
|
else /* the result will be greater than 1.0, so negative: */
|
|
return -(png_int_32)/*SAFE*/((factor - lg2 + 2048U) >> 12);
|
|
}
|
|
|
|
#if 0 /* UNUSED */
|
|
static png_int_32
|
|
png_log16bit(unsigned int x)
|
|
{
|
|
return png_log(x, 0U);
|
|
}
|
|
#endif /* UNUSED */
|
|
|
|
/* libpng 1.7.0: generalization of png_log{8,16}bit to accept an n-bit input
|
|
* value. We want to maintain 1% accuracy in linear light space. This
|
|
* corresponds to, approximately, (1*g)% in a gamma encoded space where the
|
|
* gamma encoding is 'g' (in the PNG sense, e.g. 0.45455 for sRGB). Apparently
|
|
* this requires unbounded accuracy as the gamma encoding value goes down and
|
|
* this is a problem for modern HDR data because it may require a high gamma to
|
|
* accurately encode image data over a wide dynamic range; the dynamic range of
|
|
* 16-bit linear data is only 655:1 if 1% accuracy is needed!
|
|
*
|
|
* However 16-bit gamma encoded data is still limited because PNG can only
|
|
* express gamma encoding. (A log-to-base-1.01 encoding is unlimited; a 12-bit
|
|
* value, with 4094 steps, has a dynamic range of more than 1:10^17, which
|
|
* exceeds the human eye's range of 1:10^14.)
|
|
*
|
|
* Notice that sRGB uses a 1/2.4 encoding and CIELab uses a 1/3 encoding. It is
|
|
* obvious that, if we assume a maximum D difference in the luminance of
|
|
* adjacent pixel values the dynamic range is given by the lowest pixel value
|
|
* which is D or less greater than its predecessor, so:
|
|
*
|
|
* ( P ) (1)
|
|
* (---)^(-) = D
|
|
* (P-1) (g)
|
|
*
|
|
* and the maximum dynamic range that can be achieved using M+1 separate values,
|
|
* where M+1 is 2^N-1 for an N bit value, reserving the first value for 0, is:
|
|
*
|
|
* (M) (1)
|
|
* range(R) = (-)^(-)
|
|
* (P) (g)
|
|
*
|
|
* So we can eliminate 'P' from the two equations:
|
|
*
|
|
* P = (P-1) x (D^g)
|
|
*
|
|
* D^g
|
|
* P = -----
|
|
* D^g-1
|
|
*
|
|
* (M x (D^g-1)) (1)
|
|
* R = (-----------)^(-)
|
|
* ( D^g ) (g)
|
|
*
|
|
* (M x (D^g-1)) ^ (1/g)
|
|
* = ---------------------
|
|
* D
|
|
*
|
|
* Which is a function in two variables (R and g) for a given D (maximum delta
|
|
* between two adjacent pixel values) and M (number of pixel values, controlled
|
|
* by the channel bit depth).
|
|
*
|
|
* See contrib/tools/dynamic-range.c for code exploring this function. This
|
|
* program will output the optimal gamma for a given number of bits and
|
|
* precision.
|
|
*
|
|
* The range of sensitivity of human vision is roughly as follows (this comes
|
|
* from the wikipedia article on scotopic vision):
|
|
*
|
|
* scotopic: 10^-6 to 10^-3.5 cd/m^2
|
|
* mesopic: 10^-3 to 10^0.5 cd/m^2
|
|
* photopic: 10 to 10^8 cd/m^2
|
|
*
|
|
* Giving a total range of about 1:10^14. The maximum precision at which this
|
|
* range can be achieved using 16-bit channels is about .15% using a gamma of
|
|
* 36, higher ranges are possible using higher gammas but precision is reduced.
|
|
* The range with 1% precision and 16-bit channels is 1:10^104, using a gamma of
|
|
* 240.
|
|
*
|
|
* In general the optimal gamma for n-bit channels (where 'n' is at least 7 and
|
|
* precision is .01 or less) is:
|
|
*
|
|
* 2^n * precision
|
|
* gamma = ---------------
|
|
* 2.736
|
|
*
|
|
* Or: (24000 * precision) for 16-bit data.
|
|
*
|
|
* The net effect is that we can't rely on the encoding gamma being limited to
|
|
* values around 1/2.5!
|
|
*/
|
|
static png_int_32
|
|
png_log_nbit(unsigned int x, unsigned int nbits)
|
|
{
|
|
static const png_uint_32 factors[16] =
|
|
{
|
|
4294961387U, /* 1 bit */
|
|
3869501255U, /* 2 bit */
|
|
3541367788U, /* 3 bit */
|
|
3246213428U, /* 4 bit */
|
|
2965079441U, /* 5 bit */
|
|
2690447525U, /* 6 bit */
|
|
2418950626U, /* 7 bit */
|
|
2148993476U, /* 8 bit */
|
|
1879799410U, /* 9 bit */
|
|
1610985205U, /* 10 bit */
|
|
1342360514U, /* 11 bit */
|
|
1073830475U, /* 12 bit */
|
|
805347736U, /* 13 bit */
|
|
536888641U, /* 14 bit */
|
|
268441365U, /* 15 bit */
|
|
0U /* 16 bit */
|
|
};
|
|
|
|
return png_log(x, factors[nbits-1]);
|
|
}
|
|
|
|
|
|
/* The 'exp()' case must invert the above, taking a 20-bit fixed point
|
|
* logarithmic value and returning a 16 or 8-bit number as appropriate. In
|
|
* each case only the low 16 bits are relevant - the fraction - since the
|
|
* integer bits (the top 4) simply determine a shift.
|
|
*
|
|
* The worst case is the 16-bit distinction between 65535 and 65534. This
|
|
* requires perhaps spurious accuracy in the decoding of the logarithm to
|
|
* distinguish log2(65535/65534.5) - 10^-5 or 17 bits. There is little chance
|
|
* of needing this accuracy in practice.
|
|
*
|
|
* To deal with this the following exp() function works out the exponent of the
|
|
* frational part of the logarithm by using an accurate 32-bit value from the
|
|
* top four fractional bits then multiplying in the remaining bits.
|
|
*/
|
|
static const png_uint_32
|
|
png_32bit_exp[16] =
|
|
{
|
|
/* NOTE: the first entry is deliberately set to the maximum 32-bit value. */
|
|
4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U,
|
|
3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U,
|
|
2553802834U, 2445529972U, 2341847524U, 2242560872U
|
|
};
|
|
|
|
/* Adjustment table; provided to explain the numbers in the code below. */
|
|
#if 0 /* BC CODE */
|
|
for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"}
|
|
11 44937.64284865548751208448
|
|
10 45180.98734845585101160448
|
|
9 45303.31936980687359311872
|
|
8 45364.65110595323018870784
|
|
7 45395.35850361789624614912
|
|
6 45410.72259715102037508096
|
|
5 45418.40724413220722311168
|
|
4 45422.25021786898173001728
|
|
3 45424.17186732298419044352
|
|
2 45425.13273269940811464704
|
|
1 45425.61317555035558641664
|
|
0 45425.85339951654943850496
|
|
#endif
|
|
|
|
static png_uint_32
|
|
png_exp(png_int_32 x)
|
|
/* Utility, the value 'x' must be in the range 0..0x1fffff */
|
|
{
|
|
/* Obtain a 4-bit approximation */
|
|
png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf];
|
|
|
|
/* Incorporate the low 12 bits - these decrease the returned value by
|
|
* multiplying by a number less than 1 if the bit is set. The multiplier
|
|
* is determined by the above table and the shift. Notice that the values
|
|
* converge on 45426 and this is used to allow linear interpolation of the
|
|
* low bits.
|
|
*/
|
|
if (x & 0x800)
|
|
e -= (((e >> 16) * 44938U) + 16U) >> 5;
|
|
|
|
if (x & 0x400)
|
|
e -= (((e >> 16) * 45181U) + 32U) >> 6;
|
|
|
|
if (x & 0x200)
|
|
e -= (((e >> 16) * 45303U) + 64U) >> 7;
|
|
|
|
if (x & 0x100)
|
|
e -= (((e >> 16) * 45365U) + 128U) >> 8;
|
|
|
|
if (x & 0x080)
|
|
e -= (((e >> 16) * 45395U) + 256U) >> 9;
|
|
|
|
if (x & 0x040)
|
|
e -= (((e >> 16) * 45410U) + 512U) >> 10;
|
|
|
|
/* And handle the low 6 bits in a single block. */
|
|
e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9;
|
|
|
|
/* Handle the upper bits of x, note that this works for x up to 0x1fffff but
|
|
* fails for larger or negative x, where the shift (x >> 16) exceeds 31:
|
|
*/
|
|
e >>= x >> 16;
|
|
return e;
|
|
}
|
|
|
|
#if 0 /* UNUSED */
|
|
static png_byte
|
|
png_exp8bit(png_int_32 lg2)
|
|
{
|
|
/* The input is a negative fixed point (16:16) logarithm with a useable range
|
|
* of [0.0..8.0). Clamp the value so that the output of png_exp is in the
|
|
* range (254.5/255..0.5/255):
|
|
*/
|
|
if (lg2 <= 185) /* -log2(254.5/255) */
|
|
return 255U;
|
|
|
|
else if (lg2 > 589453) /* -log2(0.5/255) */
|
|
return 0U;
|
|
|
|
else
|
|
{
|
|
/* Get a 32-bit value: */
|
|
png_uint_32 x = png_exp(lg2);
|
|
|
|
/* Convert the 32-bit value to 0..255 by multiplying by 256-1. Note that
|
|
* the second, rounding, step can't overflow because of the first,
|
|
* subtraction, step.
|
|
*/
|
|
x -= x >> 8;
|
|
return PNG_BYTE((x + 0x7fffffU) >> 24);
|
|
}
|
|
}
|
|
|
|
static png_uint_16
|
|
png_exp16bit(png_int_32 lg2)
|
|
{
|
|
if (lg2 <= 0) /* -log2(65534.5/65535) */
|
|
return 65535U;
|
|
|
|
else if (lg2 > 1114110) /* -log2(0.5/65535) */
|
|
return 0U;
|
|
|
|
else
|
|
{
|
|
/* Get a 32-bit value: */
|
|
png_uint_32 x = png_exp(lg2);
|
|
|
|
/* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */
|
|
x -= x >> 16;
|
|
return PNG_UINT_16((x + 32767U) >> 16);
|
|
}
|
|
}
|
|
#endif /* UNUSED */
|
|
|
|
static png_uint_32
|
|
png_exp_nbit(png_int_32 lg2, unsigned int n)
|
|
{
|
|
/* These pre-computed limits give the low value of lg2 at and below which
|
|
* 2^(-lg2/65536) * (2^n-1) gives (2^n-1) and the high value of lg2 above
|
|
* which 2(^-lg2/65536) * (2^n-1) gives 0:
|
|
*/
|
|
static const png_int_32 limits[16][2] =
|
|
{
|
|
{ 65535, 65535 }, /* bits = 1 */
|
|
{ 17238, 169408 }, /* bits = 2 */
|
|
{ 7006, 249518 }, /* bits = 3 */
|
|
{ 3205, 321577 }, /* bits = 4 */
|
|
{ 1537, 390214 }, /* bits = 5 */
|
|
{ 753, 457263 }, /* bits = 6 */
|
|
{ 372, 523546 }, /* bits = 7 */
|
|
{ 185, 589453 }, /* bits = 8 */
|
|
{ 92, 655175 }, /* bits = 9 */
|
|
{ 46, 720803 }, /* bits = 10 */
|
|
{ 23, 786385 }, /* bits = 11 */
|
|
{ 11, 851944 }, /* bits = 12 */
|
|
{ 5, 917492 }, /* bits = 13 */
|
|
{ 2, 983034 }, /* bits = 14 */
|
|
{ 1, 1048573 }, /* bits = 15 */
|
|
{ 0, 1114110 } /* bits = 16 */
|
|
};
|
|
|
|
/* If 'max' is 2^n-1: */
|
|
if (lg2 <= limits[n-1][0]) /* -log2((max-.5)/max) */
|
|
return (1U << n)-1U;
|
|
|
|
else if (lg2 > limits[n-1][1]) /* -log2(.5/max) */
|
|
return 0U;
|
|
|
|
else /* 'n' will be at least 2 */
|
|
{
|
|
/* Get a 32-bit value: */
|
|
png_uint_32 x = png_exp(lg2);
|
|
|
|
/* Convert the 32-bit value to 0..(2^n-1) by multiplying by 2^n-1: */
|
|
x -= x >> n;
|
|
return (x + ((1U<<(31U-n))-1U)) >> (32U-n);
|
|
}
|
|
}
|
|
#endif /* !FLOATING_ARITHMETIC */
|
|
|
|
#if 0 /* UNUSED */
|
|
static png_byte
|
|
png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val)
|
|
{
|
|
if (value == 0U)
|
|
return 0U;
|
|
|
|
else if (value >= 255U)
|
|
return 255U;
|
|
|
|
else
|
|
{
|
|
# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
|
|
/* 'value' is unsigned, ANSI-C90 requires the compiler to correctly
|
|
* convert this to a floating point value. This includes values that
|
|
* would overflow if 'value' were to be converted to 'int'.
|
|
*
|
|
* Apparently GCC, however, does an intermediate conversion to (int)
|
|
* on some (ARM) but not all (x86) platforms, possibly because of
|
|
* hardware FP limitations. (E.g. if the hardware conversion always
|
|
* assumes the integer register contains a signed value.) This results
|
|
* in ANSI-C undefined behavior for large values.
|
|
*
|
|
* Other implementations on the same machine might actually be ANSI-C90
|
|
* conformant and therefore compile spurious extra code for the large
|
|
* values.
|
|
*
|
|
* We can be reasonably sure that an unsigned to float conversion
|
|
* won't be faster than an int to float one. Therefore this code
|
|
* assumes responsibility for the undefined behavior, which it knows
|
|
* can't happen because of the check above.
|
|
*
|
|
* Note the argument to this routine is an (unsigned int) because, on
|
|
* 16-bit platforms, it is assigned a value which might be out of
|
|
* range for an (int); that would result in undefined behavior in the
|
|
* caller if the *argument* ('value') were to be declared (int).
|
|
*/
|
|
double r = 255*pow((int)/*SAFE*/value/255.,gamma_val*.00001);
|
|
if (r < .5)
|
|
return 0U;
|
|
|
|
else if (r >= 254.5)
|
|
return 255U;
|
|
|
|
r = floor(r+.5);
|
|
return (png_byte)/*SAFE*/r;
|
|
# else
|
|
png_int_32 lg2 = png_log8bit(value);
|
|
png_int_32 res;
|
|
|
|
/* Overflow in the muldiv means underflow in the calculation, this is
|
|
* OK (it happens for ridiculously high gamma).
|
|
*/
|
|
if (!png_muldiv(&res, gamma_val, lg2, PNG_FP_1))
|
|
return 0U; /* underflow */
|
|
|
|
return png_exp8bit(res);
|
|
# endif
|
|
}
|
|
}
|
|
#endif /* UNUSED */
|
|
|
|
/* libpng-1.7.0: this private function converts an n-bit input value to an
|
|
* m-bit output value.
|
|
*/
|
|
unsigned int
|
|
png_gamma_nxmbit_correct(unsigned int value, png_fixed_point gamma_val,
|
|
unsigned int n/*input bits*/, unsigned int m/*output bits */)
|
|
{
|
|
if (value == 0U)
|
|
return 0U;
|
|
|
|
else
|
|
{
|
|
unsigned int min = (1U<<n) - 1U;
|
|
unsigned int mout = (1U<<m) - 1U;
|
|
|
|
if (value >= min)
|
|
return mout;
|
|
|
|
else
|
|
{
|
|
# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
|
|
double r = value;
|
|
r /= min;
|
|
r = floor(mout * pow(r, gamma_val*.00001)+.5);
|
|
if (r < 1)
|
|
return 0U;
|
|
|
|
else if (r >= mout)
|
|
return mout;
|
|
|
|
return (unsigned int)/*SAFE*/r;
|
|
# else
|
|
png_int_32 lg2 = png_log_nbit(value, n);
|
|
png_int_32 res;
|
|
|
|
if (!png_muldiv(&res, gamma_val, lg2, PNG_FP_1))
|
|
return 0U; /* underflow */
|
|
|
|
return png_exp_nbit(res, m);
|
|
# endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0 /*UNUSED*/
|
|
static unsigned int
|
|
png_gamma_sbit_correct(unsigned int value, png_fixed_point gamma_val,
|
|
unsigned int n/*input bits*/, unsigned int sbits,
|
|
unsigned int m/*output bits */)
|
|
/* As above but the number of significant bits in 'n' is passed in. */
|
|
{
|
|
if (sbits < n)
|
|
{
|
|
value >>= (n-sbits);
|
|
n = sbits;
|
|
}
|
|
|
|
return png_gamma_nxmbit_correct(value, gamma_val, n, m);
|
|
}
|
|
#endif /*UNUSED*/
|
|
|
|
static int
|
|
push_gamma_expand(png_transformp *transform, png_transform_controlp tc,
|
|
int need_alpha)
|
|
/* Utility to push a transform to expand low-bit-depth gray and, where
|
|
* required, tRNS chunks. The caller must return immediately if this
|
|
* returns true because the init of the new transform has been run in place
|
|
* of the caller's.
|
|
*/
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
unsigned int expand = 0;
|
|
|
|
affirm(tc->init == PNG_TC_INIT_FINAL);
|
|
|
|
if (tc->bit_depth < 8U) /* low bit gray: expand to 8 bits */
|
|
expand = PNG_EXPAND_LBD_GRAY;
|
|
|
|
/* Gamma correction invalidates tRNS, so if it is being expanded and
|
|
* alpha is not being stripped expand it now.
|
|
*/
|
|
if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette &&
|
|
png_ptr->num_trans == 1 && (tc->invalid_info & PNG_INFO_tRNS) == 0)
|
|
{
|
|
if (need_alpha || (tc->expand_tRNS && !tc->strip_alpha))
|
|
expand |= PNG_EXPAND_tRNS;
|
|
|
|
else
|
|
tc->invalid_info |= PNG_INFO_tRNS;
|
|
}
|
|
|
|
if (expand == 0)
|
|
return 0; /* nothing needs to be done */
|
|
|
|
{
|
|
png_transformp tr = png_push_transform(png_ptr, sizeof (png_expand),
|
|
png_init_expand, transform, NULL/*don't run init*/);
|
|
|
|
debug(tr == *transform);
|
|
tr->args |= expand;
|
|
|
|
/* This must be run immediately, because it just got inserted where this
|
|
* transform is; this is safe, the caller must return immediately.
|
|
*/
|
|
png_init_expand(transform, tc);
|
|
affirm(tr->fn != NULL); /* because it should need to do something! */
|
|
}
|
|
|
|
return 1;
|
|
# undef png_ptr
|
|
}
|
|
|
|
/* Low bit depth gray gamma correction. The 1-bit case is a no-op because 0 and
|
|
* 1 always map to 0 and 1. The 2-bit case has the following possiblities:
|
|
*
|
|
* bits/correction: g0 g1 g2 g3 g4 g5 g6
|
|
* 00 -> 00 00 00 00 00 00 00
|
|
* 01 -> 11 10 10 01 00 00 00
|
|
* 10 -> 11 11 10 10 10 01 00
|
|
* 11 -> 11 11 11 11 11 11 11
|
|
*
|
|
* Where the breakpoints are:
|
|
*
|
|
* g0: correction <= 16595 (1 - log(2.5/3))
|
|
* g1: 16595 < correction <= 44966 (log(2.5/3)/log(2/3))
|
|
* g2: 44966 < correction <= 63092 (1 - log(1.5/3))
|
|
* g3: 63092 < correction <= 163092 (1 - log(.5/3))
|
|
* g4: 163092 < correction <= 170951 (log(1.5/3)/log(2/3))
|
|
* g5: 170951 < correction <= 441902 (log(.5/3)/log(2/3)
|
|
* g6 441902 < correction
|
|
*
|
|
* This can be done by bit-hacking on the byte values (4 pixels), given that
|
|
* the correction is fixed (indeed, it can be done on whole 32-bit values!)
|
|
*
|
|
* g0: B |= B>>1; B &= 0x55U; B |= B<<1; * either bit set
|
|
* g1: B ^= B>>1; B &= 0x55U; B += B; * one bit set
|
|
* g2: B &= (~B)>>1; B &= 0x55U; B += B; * low bit set, high bit unset
|
|
* g3: no-op
|
|
* g4: B &= (~B)>>1; B &= 0x55U; B -= B; * low bit set, high bit unset
|
|
* g5: B ^= B>>1; B &= 0x55U; B -= B; * one bit set
|
|
* g6: B &= B>>1; B &= 0x55U; B |= B<<1; * both bits set
|
|
*/
|
|
typedef struct
|
|
{
|
|
png_transform tr;
|
|
png_fixed_point correct;
|
|
png_fixed_point to_gamma;
|
|
png_uint_32 shifts; /* 1 followed by up to 4 4-bit shifts */
|
|
png_uint_32 channel_scale[4]; /* up to 4 channel scale factors */
|
|
/* These factors are used:
|
|
*
|
|
* (input >> (shifts & 0xFU) * channel_scale + SCALE_R) >> SCALE_S
|
|
*
|
|
* Where the rounding value, SCALE_R and the shift SCALE_S are dependent
|
|
* on the bit depth:
|
|
*
|
|
* SCALE_S = 32 - bit_depth range 16..31
|
|
* SCALE_R = 1 << (SCALE_S-1)
|
|
*/
|
|
unsigned int to_bit_depth;
|
|
unsigned int encode_alpha :1;
|
|
unsigned int optimize_alpha :1;
|
|
} png_transform_gamma;
|
|
|
|
static unsigned int
|
|
init_gamma_sBIT(png_transform_gamma *tr, png_transform_controlp tc)
|
|
/* Returns true if sBIT processing is required, otherwise all relevant sBIT
|
|
* values match the from (tc) bit depth.
|
|
*/
|
|
{
|
|
/* The to_bit_depth and to_gamma fields are already set, but updated values
|
|
* are needed for sBIT and the shifts and channel_scale fields must be filled
|
|
* in correctly. The do_gamma setting says whether gamma correction will be
|
|
* done, but the scale factors are filled in regardless.
|
|
*
|
|
* The general scaling equation is:
|
|
*
|
|
* ((in >> shift) * factor + round) >> (32 - to_bit_depth)
|
|
*
|
|
* 'factor' is then the rounded value of:
|
|
*
|
|
* out_max
|
|
* ------- . (1 << (32-to_bit_depth))
|
|
* in_max
|
|
*/
|
|
# define png_ptr (tc->png_ptr)
|
|
const unsigned int to_bit_depth = tr->to_bit_depth;
|
|
const png_uint_32 numerator = ((1U<<to_bit_depth)-1U) << (32U-to_bit_depth);
|
|
/* in_max depends on the number of significant bits */
|
|
const unsigned int from_bit_depth = tc->bit_depth;
|
|
|
|
/* The data in the gamma transform is stored in the order of the channels in
|
|
* the input row, which is the PNG order. It may be reversed below.
|
|
*/
|
|
png_uint_32p channel_scale = tr->channel_scale;
|
|
png_uint_32 shifts = 0U;
|
|
unsigned int count = 0U;
|
|
unsigned int need_sBIT = 0U;
|
|
|
|
if (tc->format & PNG_FORMAT_FLAG_COLOR)
|
|
{
|
|
const unsigned int sBIT = tc->sBIT_R;
|
|
|
|
if (sBIT < from_bit_depth)
|
|
need_sBIT = 1U;
|
|
|
|
debug(sBIT > 0U && sBIT <= from_bit_depth);
|
|
shifts |= (from_bit_depth - sBIT) << count;
|
|
count += 4U;
|
|
/* round the scale: */
|
|
*channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U);
|
|
}
|
|
|
|
{
|
|
const unsigned int sBIT = tc->sBIT_G;
|
|
|
|
if (sBIT < from_bit_depth)
|
|
need_sBIT = 1U;
|
|
|
|
debug(sBIT > 0U && sBIT <= from_bit_depth);
|
|
shifts |= (from_bit_depth - sBIT) << count;
|
|
count += 4U;
|
|
*channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U);
|
|
}
|
|
|
|
if (tc->format & PNG_FORMAT_FLAG_COLOR)
|
|
{
|
|
const unsigned int sBIT = tc->sBIT_B;
|
|
|
|
if (sBIT < from_bit_depth)
|
|
need_sBIT = 1U;
|
|
|
|
debug(sBIT > 0U && sBIT <= from_bit_depth);
|
|
shifts |= (from_bit_depth - sBIT) << count;
|
|
count += 4U;
|
|
/* round the scale: */
|
|
*channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U);
|
|
}
|
|
|
|
if (tc->format & PNG_FORMAT_FLAG_ALPHA)
|
|
{
|
|
const unsigned int sBIT = tc->sBIT_A;
|
|
|
|
if (sBIT < from_bit_depth)
|
|
need_sBIT = 1U;
|
|
|
|
debug(sBIT > 0U && sBIT <= from_bit_depth);
|
|
shifts |= (from_bit_depth - sBIT) << count;
|
|
count += 4U;
|
|
/* round the scale: */
|
|
*channel_scale++ = (numerator + (1U<<(sBIT-1U))) / ((1U << sBIT)-1U);
|
|
}
|
|
|
|
tr->shifts = shifts | (1U << count);
|
|
|
|
return need_sBIT;
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
reverse_gamma_sBIT(png_transform_gamma *tr)
|
|
{
|
|
/* This is called for the 'down' gamma implementations, they read the shifts
|
|
* and the channel scales in reverse, so:
|
|
*/
|
|
png_uint_32 shifts = tr->shifts;
|
|
png_uint_32 scales[4U];
|
|
unsigned int count = 0U;
|
|
|
|
tr->shifts = 1U;
|
|
|
|
while (shifts != 1U)
|
|
{
|
|
scales[3U-count] = tr->channel_scale[count];
|
|
++count;
|
|
tr->shifts <<= 4;
|
|
tr->shifts |= shifts & 0xFU;
|
|
shifts >>= 4;
|
|
}
|
|
|
|
memcpy(tr->channel_scale, scales+(4U-count), count * sizeof (png_uint_32));
|
|
}
|
|
|
|
static void
|
|
png_do_gamma8_up(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_transform_gamma *tr =
|
|
png_transform_cast(png_transform_gamma, *transform);
|
|
const png_fixed_point correct = tr->correct;
|
|
const unsigned int bit_depth = tr->to_bit_depth;
|
|
const png_uint_32 shifts = tr->shifts;
|
|
|
|
affirm(tc->bit_depth == 8U);
|
|
affirm(tr->shifts != 0U/*uninitialized*/);
|
|
debug((shifts & 0x8888U) == 0U); /* all shifts 7 or less */
|
|
debug(!tr->encode_alpha && !tr->optimize_alpha); /* only set for 16 bits */
|
|
|
|
tc->sp = dp;
|
|
tc->bit_depth = bit_depth;
|
|
tc->gamma = tr->to_gamma;
|
|
|
|
/* Handle the <8 bit output case differently because there can be no alpha
|
|
* channel.
|
|
*/
|
|
if (bit_depth < 8U)
|
|
{
|
|
const unsigned int shift = shifts & 0xFU;
|
|
unsigned int bits = 8U;
|
|
unsigned int ob = 0U;
|
|
|
|
debug((shifts >> 4) == 1U && shift < 8U);
|
|
affirm(PNG_TC_CHANNELS(*tc) == 1);
|
|
|
|
do
|
|
{
|
|
const unsigned int inb = png_gamma_nxmbit_correct(
|
|
*sp++ >> shift, correct, 8U-shift, bit_depth);
|
|
bits -= bit_depth;
|
|
ob = ob | (inb << bits);
|
|
if (bits == 0U)
|
|
bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
|
|
}
|
|
while (sp < ep);
|
|
|
|
if (bits < 8U)
|
|
*dp++ = PNG_BYTE(ob);
|
|
}
|
|
|
|
else /* 8-bit --> 8-bit */
|
|
{
|
|
png_uint_32 alpha_scale;
|
|
const unsigned int channels = PNG_TC_CHANNELS(*tc);
|
|
unsigned int channel, alpha;
|
|
|
|
debug(bit_depth == 8U && (shifts >> (4*channels)) == 1U);
|
|
|
|
/* The alpha channel is always last, so if present checking against the
|
|
* top bits of 'channels' works because of the 1U shibboleth at the end.
|
|
*/
|
|
if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0)
|
|
alpha_scale = alpha = 0U;
|
|
|
|
else
|
|
{
|
|
alpha = shifts >> (4U*(channels-1U));
|
|
alpha_scale = tr->channel_scale[channels-1U];
|
|
}
|
|
|
|
channel = 1U;
|
|
|
|
do
|
|
{
|
|
unsigned int inb = *sp++, shift;
|
|
|
|
if (channel == 1U)
|
|
channel = shifts;
|
|
|
|
shift = channel & 0xFU;
|
|
inb >>= shift;
|
|
|
|
/* The alpha channel is not gamma encoded but it may need some
|
|
* appropriate scaling.
|
|
*/
|
|
if (channel == alpha)
|
|
inb = (inb * alpha_scale + 0x800000U) >> 24;
|
|
|
|
else
|
|
inb = png_gamma_nxmbit_correct(inb, correct, 8U-shift, 8U);
|
|
|
|
channel >>= 4; /* for the next channel, or the shibboleth */
|
|
*dp++ = PNG_BYTE(inb);
|
|
}
|
|
while (sp < ep);
|
|
|
|
debug(channel == 1U);
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_gamma16_up(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 1U/*safety*/;
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_transform_gamma *tr =
|
|
png_transform_cast(png_transform_gamma, *transform);
|
|
const png_fixed_point correct = tr->correct;
|
|
const unsigned int bit_depth = tr->to_bit_depth;
|
|
const png_uint_32 shifts = tr->shifts;
|
|
|
|
affirm(tc->bit_depth == 16U);
|
|
affirm(tr->shifts != 0U/*uninitialized*/);
|
|
debug(!tr->optimize_alpha);
|
|
|
|
/* This is exactly the same as above but the input has 16 bits per component,
|
|
* not 8.
|
|
*/
|
|
tc->sp = dp;
|
|
tc->bit_depth = bit_depth;
|
|
tc->gamma = tr->to_gamma;
|
|
|
|
/* Handle the <8 bit output case differently, the input cannot be color (at
|
|
* present) and, if there is an alpha channel, then it is for the
|
|
* low-bit-depth gray input case and we expect the alpha to be transparent.
|
|
*/
|
|
if (bit_depth < 8U)
|
|
{
|
|
const unsigned int shift = shifts & 0xFU;
|
|
unsigned int bits = 8U;
|
|
unsigned int ob = 0U;
|
|
|
|
affirm((tc->format & PNG_FORMAT_FLAG_COLOR) == 0U);
|
|
|
|
if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0U)
|
|
{
|
|
debug((shifts >> 4) == 1U && shift < 16U);
|
|
debug(!tr->encode_alpha && !tr->optimize_alpha);
|
|
|
|
do
|
|
{
|
|
unsigned int inb = *sp++ << 8; /* high bits first */
|
|
inb = png_gamma_nxmbit_correct(
|
|
(inb + *sp++) >> shift, correct, 16U-shift, bit_depth);
|
|
|
|
bits -= bit_depth;
|
|
ob = ob | (inb << bits);
|
|
if (bits == 0U)
|
|
bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
|
|
}
|
|
while (sp < ep);
|
|
|
|
UNTESTED
|
|
}
|
|
|
|
else /* low bit GA intermediate format */
|
|
{
|
|
debug((shifts >> 8) == 1U && shift < 16U);
|
|
debug(!tr->encode_alpha && !tr->optimize_alpha);
|
|
debug(tc->transparent_alpha);
|
|
|
|
/* Gray is first then the alpha component, the alpha component is just
|
|
* mapped to 0 or 1.
|
|
*/
|
|
do
|
|
{
|
|
unsigned int gray = *sp++ << 8; /* high bits first */
|
|
unsigned int alpha;
|
|
gray += *sp++;
|
|
|
|
alpha = (*sp++ << 8);
|
|
alpha += *sp++;
|
|
|
|
if (alpha == 0U)
|
|
gray = 0U; /* will be replaced later */
|
|
|
|
else
|
|
{
|
|
gray = png_gamma_nxmbit_correct(gray >> shift, correct,
|
|
16U-shift, bit_depth);
|
|
debug(alpha == 65535U);
|
|
alpha = (1U << bit_depth)-1U;
|
|
}
|
|
|
|
bits -= bit_depth;
|
|
ob = ob | (gray << bits);
|
|
bits -= bit_depth;
|
|
ob = ob | (alpha << bits);
|
|
|
|
if (bits == 0U)
|
|
bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
|
|
}
|
|
while (sp < ep-2U);
|
|
}
|
|
|
|
if (bits < 8U)
|
|
*dp++ = PNG_BYTE(ob);
|
|
|
|
debug(sp == ep+1U);
|
|
}
|
|
|
|
else
|
|
{
|
|
png_uint_32 alpha_scale;
|
|
const unsigned int channels = PNG_TC_CHANNELS(*tc);
|
|
unsigned int channel, alpha;
|
|
|
|
debug((bit_depth == 8U || bit_depth == 16U) &&
|
|
(shifts >> (4*channels)) == 1U);
|
|
|
|
/* Note that 'encode_alpha' turns on gamma encoding of the alpha
|
|
* channel (and this is a really weird operation!)
|
|
*/
|
|
if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 || tr->encode_alpha)
|
|
alpha_scale = alpha = 0U;
|
|
|
|
else
|
|
{
|
|
alpha = shifts >> (4U*(channels-1U));
|
|
alpha_scale = tr->channel_scale[channels-1U];
|
|
}
|
|
|
|
channel = 1U;
|
|
|
|
if (bit_depth == 16U)
|
|
{
|
|
do
|
|
{
|
|
unsigned int inb = *sp++ << 8, shift;
|
|
inb += *sp++;
|
|
|
|
if (channel == 1U)
|
|
channel = shifts;
|
|
|
|
shift = channel & 0xFU;
|
|
inb >>= shift;
|
|
|
|
/* The 16-16bit scaling factor equation may be off-by-1 but this
|
|
* hardly matters for alpha or for gamma operations.
|
|
*/
|
|
if (channel == alpha)
|
|
inb = (inb * alpha_scale + 0x8000U) >> 16;
|
|
|
|
else
|
|
inb = png_gamma_nxmbit_correct(inb, correct, 16U-shift, 16U);
|
|
|
|
channel >>= 4; /* for the next channel, or the shibboleth */
|
|
*dp++ = PNG_BYTE(inb >> 8);
|
|
*dp++ = PNG_BYTE(inb);
|
|
}
|
|
while (sp < ep);
|
|
|
|
debug(channel == 1U && sp == ep+1U);
|
|
}
|
|
|
|
else /* bit_depth == 8U */
|
|
{
|
|
do
|
|
{
|
|
unsigned int inb = *sp++ << 8, shift;
|
|
inb += *sp++;
|
|
|
|
if (channel == 1U)
|
|
channel = shifts;
|
|
|
|
shift = channel & 0xFU;
|
|
inb >>= shift;
|
|
|
|
if (channel == alpha)
|
|
inb = (inb * alpha_scale + 0x800000U) >> 24;
|
|
|
|
else
|
|
inb = png_gamma_nxmbit_correct(inb, correct, 16U-shift, 8U);
|
|
|
|
channel >>= 4; /* for the next channel, or the shibboleth */
|
|
*dp++ = PNG_BYTE(inb);
|
|
}
|
|
while (sp < ep);
|
|
|
|
debug(channel == 1U && sp == ep+1U);
|
|
}
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
|
|
static void
|
|
png_do_gamma16_up_optimize(png_transformp *transform, png_transform_controlp tc)
|
|
/* As above, but the alpha channel is 'optimized' */
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_transform_gamma *tr =
|
|
png_transform_cast(png_transform_gamma, *transform);
|
|
const png_fixed_point correct = tr->correct;
|
|
|
|
/* The input always as 16 bits, the output 8 or 16. There is always an alpha
|
|
* channel and it is converted to the 'optimized' form, where pixels with
|
|
* alpha not 0.0 or 1.0 are left in linear form (not gamma corrected.) Where
|
|
* bit depth convertion is required it is from 16-bits to 8-bits and the
|
|
* DIV257 macro can be used.
|
|
*
|
|
* The following affirms and NOT_REACHED cases are consequences of the way
|
|
* the background (compose) code works:
|
|
*/
|
|
affirm(tr->optimize_alpha && !tr->encode_alpha && tc->bit_depth == 16U);
|
|
|
|
/* TODO: split this into separate functions */
|
|
switch (tr->to_bit_depth)
|
|
{
|
|
case 8U: /* 16-bit --> 8-bit */
|
|
tc->sp = dp;
|
|
tc->bit_depth = 8U;
|
|
tc->gamma = tr->to_gamma;
|
|
|
|
switch (PNG_TC_CHANNELS(*tc))
|
|
{
|
|
case 2:/* GA */
|
|
debug(tr->shifts == 0x100U);
|
|
ep -= 3U; /*SAFETY*/
|
|
|
|
do
|
|
{
|
|
png_uint_32 alpha = PNG_DIV257((sp[2] << 8) + sp[3]);
|
|
|
|
switch (alpha)
|
|
{
|
|
case 0U:
|
|
dp[1] = dp[0] = 0U;
|
|
break;
|
|
|
|
default: /* optimized case: linear color data */
|
|
dp[0] = png_check_byte(png_ptr,
|
|
PNG_DIV257((sp[0] << 8) + sp[1]));
|
|
dp[1] = PNG_BYTE(alpha);
|
|
break;
|
|
|
|
case 255U: /* opaque pixels are encoded */
|
|
dp[0] = PNG_BYTE(png_gamma_nxmbit_correct(
|
|
(sp[0] << 8) + sp[1], correct, 16U, 8U));
|
|
dp[1] = 255U;
|
|
break;
|
|
}
|
|
|
|
sp += 4U;
|
|
dp += 2U;
|
|
}
|
|
while (sp < ep);
|
|
|
|
debug(sp == ep+3U);
|
|
break;
|
|
|
|
case 4:/* RGBA */
|
|
debug(tr->shifts == 0x10000U);
|
|
ep -= 7U; /*SAFETY*/
|
|
|
|
do
|
|
{
|
|
png_uint_32 alpha = PNG_DIV257((sp[6] << 8) + sp[7]);
|
|
|
|
switch (alpha)
|
|
{
|
|
case 0U:
|
|
memset(dp, 0U, 4U);
|
|
break;
|
|
|
|
default: /* optimized case: linear color data */
|
|
dp[0] = PNG_BYTE(PNG_DIV257((sp[0] << 8) + sp[1]));
|
|
dp[1] = PNG_BYTE(PNG_DIV257((sp[2] << 8) + sp[3]));
|
|
dp[2] = PNG_BYTE(PNG_DIV257((sp[4] << 8) + sp[5]));
|
|
dp[3] = PNG_BYTE(alpha);
|
|
break;
|
|
|
|
case 255U: /* opaque pixels are encoded */
|
|
dp[0] = PNG_BYTE(png_gamma_nxmbit_correct(
|
|
(sp[0] << 8) + sp[1], correct, 16U, 8U));
|
|
dp[1] = PNG_BYTE(png_gamma_nxmbit_correct(
|
|
(sp[2] << 8) + sp[3], correct, 16U, 8U));
|
|
dp[2] = PNG_BYTE(png_gamma_nxmbit_correct(
|
|
(sp[4] << 8) + sp[5], correct, 16U, 8U));
|
|
dp[3] = 255U;
|
|
break;
|
|
}
|
|
|
|
sp += 8U;
|
|
dp += 4U;
|
|
}
|
|
while (sp < ep);
|
|
|
|
debug(sp == ep+7U);
|
|
break;
|
|
|
|
default:
|
|
NOT_REACHED;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 16: /* 16-bit to 16-bit */
|
|
tc->sp = dp;
|
|
tc->bit_depth = 16U;
|
|
tc->gamma = tr->to_gamma;
|
|
|
|
switch (PNG_TC_CHANNELS(*tc))
|
|
{
|
|
case 2:/* GA */
|
|
debug(tr->shifts == 0x100U);
|
|
ep -= 3U; /*SAFETY*/
|
|
|
|
do
|
|
{
|
|
unsigned int alpha = (sp[2] << 8) + sp[3];
|
|
|
|
switch (alpha)
|
|
{
|
|
case 0U:
|
|
memset(dp, 0U, 4U);
|
|
break;
|
|
|
|
default: /* optimized case: linear color data */
|
|
if (dp != sp)
|
|
{
|
|
memcpy(dp, sp, 4U);
|
|
UNTESTED
|
|
}
|
|
break;
|
|
|
|
case 65535U: /* opaque pixels are encoded */
|
|
{
|
|
unsigned int gray = png_gamma_nxmbit_correct(
|
|
(sp[0] << 8) + sp[1], correct, 16U, 16U);
|
|
dp[0] = PNG_BYTE(gray >> 8);
|
|
dp[1] = PNG_BYTE(gray);
|
|
}
|
|
dp[3] = dp[2] = 255U;
|
|
break;
|
|
}
|
|
|
|
sp += 4U;
|
|
dp += 4U;
|
|
}
|
|
while (sp < ep);
|
|
|
|
debug(sp == ep+3U);
|
|
break;
|
|
|
|
case 4:/* RGBA */
|
|
debug(tr->shifts == 0x10000U);
|
|
ep -= 7U; /*SAFETY*/
|
|
|
|
do
|
|
{
|
|
unsigned int alpha = (sp[6] << 8) + sp[7];
|
|
|
|
switch (alpha)
|
|
{
|
|
case 0U:
|
|
memset(dp, 0U, 8U);
|
|
break;
|
|
|
|
default: /* optimized case: linear color data */
|
|
if (dp != sp)
|
|
{
|
|
memcpy(dp, sp, 8U);
|
|
UNTESTED
|
|
}
|
|
break;
|
|
|
|
case 65535U: /* opaque pixels are encoded */
|
|
{
|
|
unsigned int c = png_gamma_nxmbit_correct(
|
|
(sp[0] << 8) + sp[1], correct, 16U, 16U);
|
|
dp[0] = PNG_BYTE(c >> 8);
|
|
dp[1] = PNG_BYTE(c);
|
|
|
|
c = png_gamma_nxmbit_correct(
|
|
(sp[2] << 8) + sp[3], correct, 16U, 16U);
|
|
dp[2] = PNG_BYTE(c >> 8);
|
|
dp[3] = PNG_BYTE(c);
|
|
|
|
c = png_gamma_nxmbit_correct(
|
|
(sp[4] << 8) + sp[5], correct, 16U, 16U);
|
|
dp[4] = PNG_BYTE(c >> 8);
|
|
dp[5] = PNG_BYTE(c);
|
|
}
|
|
dp[7] = dp[6] = 255U;
|
|
break;
|
|
}
|
|
|
|
sp += 8U;
|
|
dp += 8U;
|
|
}
|
|
while (sp < ep);
|
|
|
|
debug(sp == ep+7U);
|
|
break;
|
|
|
|
default:
|
|
NOT_REACHED;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
NOT_REACHED;
|
|
break;
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
#endif /* READ_ALPHA_MODE */
|
|
|
|
static void
|
|
png_do_scale16_up(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_transform_gamma *tr =
|
|
png_transform_cast(png_transform_gamma, *transform);
|
|
const unsigned int bit_depth = tr->to_bit_depth;
|
|
|
|
affirm(tc->bit_depth == 16U && bit_depth < 8U);
|
|
affirm(tr->shifts != 0U/*uninitialized*/);
|
|
|
|
/* This is exactly the same as above but without the gamma correction and
|
|
* without the 8-bit target support. The code handles one or two channels,
|
|
* but the result is not a PNG format unless the number of channels is just
|
|
* 1 (grayscale).
|
|
*
|
|
* For multi-channel low bit depth the channels are packed into bytes using
|
|
* the standard PNG big-endian packing.
|
|
*/
|
|
affirm((tc->format & PNG_FORMAT_FLAG_COLOR) == 0);
|
|
/* The alpha shift is actually ignored; at present we only get here with an
|
|
* alpha channel if it is to be removed for transparent alpha processing.
|
|
*/
|
|
debug(tc->format & PNG_FORMAT_FLAG_ALPHA ?
|
|
(tr->shifts >> 8) == 1U : (tr->shifts >> 4) == 1U);
|
|
debug(tc->transparent_alpha);
|
|
|
|
tc->sp = dp;
|
|
/* This is a pure scaling operation so sBIT is not invalidated or altered. */
|
|
tc->bit_depth = bit_depth;
|
|
|
|
/* TODO: maybe do this properly and use the alpha shift, but only the top bit
|
|
* matters.
|
|
*/
|
|
{
|
|
const unsigned int shift = tr->shifts & 0xFU;
|
|
const png_uint_32 factor = tr->channel_scale[0];
|
|
const png_uint_32 round = 1U << (31U-bit_depth);
|
|
unsigned int bits = 8U;
|
|
unsigned int ob = 0U;
|
|
|
|
do
|
|
{
|
|
png_uint_32 inb = *sp++ << 8; /* high bits first */
|
|
inb += *sp++;
|
|
|
|
inb = ((inb >> shift) * factor + round) >> (32U-bit_depth);
|
|
bits -= bit_depth;
|
|
ob = ob | (inb << bits);
|
|
if (bits == 0U)
|
|
bits = 8U, *dp++ = PNG_BYTE(ob), ob = 0U;
|
|
}
|
|
while (sp < ep);
|
|
|
|
if (bits < 8U)
|
|
*dp++ = PNG_BYTE(ob);
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_gamma8_down(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep ep = dp + 1U/*safety*/;
|
|
png_transform_gamma *tr =
|
|
png_transform_cast(png_transform_gamma, *transform);
|
|
const png_fixed_point correct = tr->correct;
|
|
const png_uint_32 shifts = tr->shifts;
|
|
|
|
affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U);
|
|
affirm(tr->shifts != 0U/*uninitialized*/);
|
|
debug((shifts & 0x8888U) == 0U); /* all shifts 7 or less */
|
|
debug(!tr->encode_alpha && !tr->optimize_alpha); /* only set for 16 bits */
|
|
|
|
sp += PNG_TC_ROWBYTES(*tc);
|
|
tc->sp = dp;
|
|
tc->bit_depth = tr->to_bit_depth;
|
|
tc->gamma = tr->to_gamma;
|
|
dp += PNG_TC_ROWBYTES(*tc);
|
|
|
|
{
|
|
png_uint_32 alpha_scale;
|
|
unsigned int channel, alpha;
|
|
|
|
debug((shifts >> (4*PNG_TC_CHANNELS(*tc))) == 1U);
|
|
|
|
/* We are going down so alpha, if present, is first. Notice that the init
|
|
* routine has to reverse both 'shifts' and 'channel_scale' for the _down
|
|
* cases.
|
|
*/
|
|
if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0)
|
|
alpha_scale = alpha = 0U;
|
|
|
|
else
|
|
{
|
|
alpha = shifts;
|
|
alpha_scale = tr->channel_scale[0U];
|
|
}
|
|
|
|
channel = 1U;
|
|
|
|
do /* 8-bit --> 16-bit */
|
|
{
|
|
unsigned int inb = *--sp, shift;
|
|
|
|
if (channel == 1U)
|
|
channel = shifts;
|
|
|
|
shift = channel & 0xFU;
|
|
inb >>= shift;
|
|
|
|
if (channel == alpha) /* unencoded alpha, must scale */
|
|
inb = (inb * alpha_scale + 0x8000U) >> 16;
|
|
|
|
else
|
|
inb = png_gamma_nxmbit_correct(inb, correct, 8U-shift, 16U);
|
|
|
|
channel >>= 4;
|
|
|
|
*--dp = PNG_BYTE(inb);
|
|
*--dp = PNG_BYTE(inb >> 8);
|
|
}
|
|
while (dp > ep);
|
|
|
|
debug(channel == 1U && dp == ep-1U);
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_expand8_down(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep ep = dp + 1U/*safety*/;
|
|
png_transform_gamma *tr =
|
|
png_transform_cast(png_transform_gamma, *transform);
|
|
const png_uint_32 shifts = tr->shifts;
|
|
|
|
affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U);
|
|
affirm(tr->shifts != 0U/*uninitialized*/);
|
|
|
|
sp += PNG_TC_ROWBYTES(*tc);
|
|
tc->sp = dp;
|
|
tc->bit_depth = 16U;
|
|
dp += PNG_TC_ROWBYTES(*tc);
|
|
|
|
{
|
|
png_uint_32 channel = 1U;
|
|
png_const_uint_32p scale = 0U;
|
|
|
|
do /* 8-bit -> 16-bit */
|
|
{
|
|
unsigned int inb = *--sp, shift;
|
|
|
|
if (channel == 1U)
|
|
channel = shifts, scale = tr->channel_scale;
|
|
|
|
shift = channel & 0xFU;
|
|
channel >>= 4;
|
|
inb >>= shift;
|
|
inb = (inb * *scale++ + 0x8000U) >> 16;
|
|
/* dp starts beyond the end: */
|
|
*--dp = PNG_BYTE(inb);
|
|
*--dp = PNG_BYTE(inb >> 8);
|
|
}
|
|
while (dp > ep);
|
|
|
|
debug(channel == 1U && dp == ep-1U);
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_expand8_down_fast(png_transformp *transform, png_transform_controlp tc)
|
|
/* Optimized version of the above for when the sBIT settings are all a full 8
|
|
* bits (the normal case).
|
|
*/
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep ep = dp + 1U/*safety*/;
|
|
png_transform_gamma *tr =
|
|
png_transform_cast(png_transform_gamma, *transform);
|
|
|
|
affirm(tc->bit_depth == 8U && tr->to_bit_depth == 16U);
|
|
affirm(tr->shifts != 0U/*uninitialized*/);
|
|
|
|
sp += PNG_TC_ROWBYTES(*tc);
|
|
tc->sp = dp;
|
|
tc->bit_depth = 16U;
|
|
dp += PNG_TC_ROWBYTES(*tc);
|
|
|
|
do
|
|
dp -= 2, dp[0] = dp[1] = *--sp;
|
|
while (dp > ep);
|
|
|
|
debug(dp == ep-1U);
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_init_gamma_uncached(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_gamma *tr =
|
|
png_transform_cast(png_transform_gamma, *transform);
|
|
|
|
debug(tc->init == PNG_TC_INIT_FINAL);
|
|
|
|
/* Set this first; the result says if the sBIT data is significant, but it is
|
|
* ignored here.
|
|
*/
|
|
(void)init_gamma_sBIT(tr, tc);
|
|
|
|
/* If png_set_alpha_mode is called but no background processing needs to be
|
|
* done (because there is no alpha channel or tRNS) we get to here with
|
|
* potentially spurious alpha mode flags.
|
|
*/
|
|
if (!(tc->format & PNG_FORMAT_FLAG_ALPHA))
|
|
tr->encode_alpha = tr->optimize_alpha = 0U;
|
|
|
|
/* Use separate functions for the two input depths but not for the five
|
|
* possible output depths and four channel counts.
|
|
*/
|
|
if (tc->bit_depth == 8U)
|
|
{
|
|
if (tr->to_bit_depth <= 8U)
|
|
tr->tr.fn = png_do_gamma8_up;
|
|
|
|
else
|
|
{
|
|
debug(tr->to_bit_depth == 16U);
|
|
reverse_gamma_sBIT(tr);
|
|
tr->tr.fn = png_do_gamma8_down;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
affirm(tc->bit_depth == 16U);
|
|
# ifdef PNG_READ_ALPHA_MODE_SUPPORTED
|
|
if (!tr->optimize_alpha)
|
|
tr->tr.fn = png_do_gamma16_up;
|
|
else
|
|
tr->tr.fn = png_do_gamma16_up_optimize;
|
|
# else /* !READ_ALPHA_MODE */
|
|
tr->tr.fn = png_do_gamma16_up;
|
|
# endif /* !READ_ALPHA_MODE */
|
|
}
|
|
|
|
/* Since the 'do' routines always perform gamma correction they will always
|
|
* expand the significant bits to the full output bit depth.
|
|
*/
|
|
tc->invalid_info |= PNG_INFO_sBIT;
|
|
tc->bit_depth = tr->to_bit_depth;
|
|
tc->sBIT_R = tc->sBIT_G = tc->sBIT_B =
|
|
png_check_byte(png_ptr, tc->bit_depth);
|
|
if (tr->encode_alpha)
|
|
tc->sBIT_A = tc->sBIT_G;
|
|
tc->gamma = tr->to_gamma;
|
|
# undef png_ptr
|
|
}
|
|
|
|
#ifdef PNG_READ_sBIT_SUPPORTED
|
|
static unsigned int
|
|
tc_sBIT(png_const_transform_controlp tc)
|
|
/* Determine the maximum number of significant bits in the row at this point.
|
|
* This uses the png_struct::sig_bit field if it has not been invalidated,
|
|
* otherwise it just returns the current bit depth.
|
|
*/
|
|
{
|
|
const png_structrp png_ptr = tc->png_ptr;
|
|
unsigned int bit_depth = tc->bit_depth;
|
|
|
|
if ((tc->invalid_info & PNG_INFO_sBIT) == 0U)
|
|
{
|
|
/* Normally the bit depth will not have been changed from the original PNG
|
|
* depth, but it currently is changed by the grayscale expand to 8 bits,
|
|
* an operation which doesn't invalidate sBIT.
|
|
*/
|
|
unsigned int sBIT;
|
|
|
|
if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0U)
|
|
{
|
|
/* Must use the largest of the sBIT depths, except that unset values
|
|
* take priority.
|
|
*/
|
|
sBIT = png_ptr->sig_bit.red && png_ptr->sig_bit.green &&
|
|
png_ptr->sig_bit.blue;
|
|
|
|
if (sBIT != 0U)
|
|
{
|
|
sBIT = png_ptr->sig_bit.red;
|
|
|
|
if (png_ptr->sig_bit.green > sBIT)
|
|
sBIT = png_ptr->sig_bit.green;
|
|
if (png_ptr->sig_bit.blue > sBIT)
|
|
sBIT = png_ptr->sig_bit.blue;
|
|
}
|
|
}
|
|
|
|
else
|
|
sBIT = png_ptr->sig_bit.gray;
|
|
|
|
if (sBIT > 0U && sBIT < bit_depth)
|
|
bit_depth = sBIT;
|
|
}
|
|
|
|
return bit_depth;
|
|
}
|
|
#else /* !READ_sBIT */
|
|
# define tc_sBIT(tc) ((tc)->bit_depth)
|
|
#endif /* READ_sBIT */
|
|
|
|
static void
|
|
png_init_gamma(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
const png_structrp png_ptr = tc->png_ptr;
|
|
png_transform_gamma *tr =
|
|
png_transform_cast(png_transform_gamma, *transform);
|
|
|
|
if (tc->init == PNG_TC_INIT_FORMAT)
|
|
{
|
|
/* This should only happen for the final encode gamma transform, which
|
|
* never initializes the target bit depth (see png_set_gamma and
|
|
* png_set_alpha_mode). The affirm is required here; in we can't continue
|
|
* safely if the bit depth has been set somehow.
|
|
*/
|
|
debug(tr->tr.order == PNG_TR_GAMMA_ENCODE);
|
|
affirm(tr->to_gamma > 0 && tr->to_bit_depth == 0U);
|
|
|
|
/* At this point the output gamma should not have been set yet: */
|
|
debug(png_ptr->row_gamma == 0);
|
|
|
|
/* The following must be true; png_set_gamma and png_set_alpha_mode set
|
|
* (or default) the PNG gamma and other routines that insert a gamma
|
|
* transform must only do in PNG_TC_INIT_FINAL:
|
|
*/
|
|
debug(tc->gamma > 0);
|
|
|
|
/* At this point the data gamma must be updated so that we get the correct
|
|
* png_struct::row_gamma at the end of the init:
|
|
*/
|
|
tc->gamma = tr->to_gamma;
|
|
|
|
/* For safety invalidate the sBIT information too; we don't know yet
|
|
* whether a gamma transform will be required but if it is the sBIT
|
|
* information becomes invalid.
|
|
*/
|
|
tc->invalid_info |= PNG_INFO_sBIT;
|
|
}
|
|
|
|
else /* PNG_TC_INIT_FINAL */
|
|
{
|
|
/* It is very bad if we get here when processing a row: */
|
|
affirm(tc->init == PNG_TC_INIT_FINAL && png_ptr->row_bit_depth > 0);
|
|
|
|
/* There are three cases:
|
|
*
|
|
* 1) Gamma correction is required, output bit depth may need to be
|
|
* defaulted.
|
|
* 2) Gamma correction is not required but a bit depth change is
|
|
* necessary.
|
|
* 3) Neither is required; the transform can be eliminated.
|
|
*
|
|
* First default the bit depth if it is not already set. Note that if the
|
|
* output is a palette then 'row_bit_depth' refers to the palette size and
|
|
* 8U must be used here. tc->palette is irrelevant; it only tells us that
|
|
* the data came from a palette.
|
|
*/
|
|
if (tr->to_bit_depth == 0)
|
|
{
|
|
if ((png_ptr->row_format & PNG_FORMAT_FLAG_COLORMAP) != 0U)
|
|
tr->to_bit_depth = 8U;
|
|
|
|
else
|
|
tr->to_bit_depth = png_ptr->row_bit_depth;
|
|
}
|
|
|
|
/* (1); is gamma correction required? If tc->gamma is 0 at this point it
|
|
* is not, but then the png_struct::row_gamma should be 0 too.
|
|
*/
|
|
implies(tc->gamma == 0, png_ptr->row_gamma == 0);
|
|
implies(tr->to_gamma == 0, tc->gamma == 0);
|
|
|
|
if (!png_gamma_equal(png_ptr, tc->gamma, tr->to_gamma, &tr->correct,
|
|
tc_sBIT(tc)))
|
|
{
|
|
/* First make sure the input doesn't have a tRNS chunk which needs to
|
|
* be expanded now; if it does push_gamma_expand will push an
|
|
* appropriate transform *before* this one and we need to return
|
|
* immediately (the caller will call back to this function).
|
|
*/
|
|
if (push_gamma_expand(transform, tc, 0/*need alpha*/))
|
|
{
|
|
affirm(tc->bit_depth >= 8U &&
|
|
(tc->invalid_info & PNG_INFO_tRNS) != 0U &&
|
|
*transform != &tr->tr);
|
|
return;
|
|
}
|
|
|
|
debug(*transform == &tr->tr && tc->bit_depth >= 8U);
|
|
|
|
/* The format is now 8 or 16-bit G, GA, RGB or RGBA and gamma
|
|
* correction is required.
|
|
*/
|
|
png_init_gamma_uncached(transform, tc);
|
|
/* TODO: implement caching for the !tc->caching cases! */
|
|
return;
|
|
}
|
|
|
|
/* The cases where the two gamma values are close enough to be considered
|
|
* equal. The code lies about the gamma; this prevents apps and the
|
|
* simplified API getting into loops or bad conditions because the gamma
|
|
* was not set to the expected value.
|
|
*
|
|
* Note that png_transform_control::gamma is only set here if both the
|
|
* input and output gamma values are known, otherwise the transform
|
|
* introduces a spurious know gamma value.
|
|
*/
|
|
if (tr->to_gamma > 0 && tc->gamma > 0)
|
|
tc->gamma = tr->to_gamma;
|
|
|
|
if (tr->to_bit_depth > tc->bit_depth)
|
|
{
|
|
/* This is either the to-linear operation, in which case the expected
|
|
* bit depth is 16U, or it is the final encode in the case where an
|
|
* 'expand' operation was also specified.
|
|
*
|
|
* We don't care about the PNG_TR_GAMMA_ENCODE case because we know
|
|
* that there has to be an expand operation further down the pipeline.
|
|
*/
|
|
if (tr->tr.order < PNG_TR_GAMMA_ENCODE)
|
|
{
|
|
affirm(tr->to_bit_depth == 16U);
|
|
|
|
if (push_gamma_expand(transform, tc, 0/*need alpha*/))
|
|
{
|
|
affirm(tc->bit_depth == 8U &&
|
|
(tc->invalid_info & PNG_INFO_tRNS) != 0U &&
|
|
*transform != &tr->tr);
|
|
return;
|
|
}
|
|
|
|
debug(*transform == &tr->tr);
|
|
affirm(tc->bit_depth == 8U); /* if 16U we would not be here! */
|
|
|
|
/* not using byte_ops here, but if there is no sBIT required
|
|
* (normally the case) the fast code can be used:
|
|
*/
|
|
if (init_gamma_sBIT(tr, tc))
|
|
tr->tr.fn = png_do_expand8_down;
|
|
|
|
else
|
|
tr->tr.fn = png_do_expand8_down_fast;
|
|
|
|
tc->bit_depth = 16U;
|
|
}
|
|
|
|
else /* PNG_TR_GAMMA_ENCODE: nothing need be done */
|
|
tr->tr.fn = NULL;
|
|
}
|
|
|
|
else if (tr->to_bit_depth < tc->bit_depth)
|
|
{
|
|
/* No gamma correction but bit depth *reduction* is required. Expect
|
|
* the 'from' bit depth to always be 16, otherwise this transform
|
|
* should not have been pushed. Also expect this to be the gamma
|
|
* 'encode' operation at the end of the arithmetic.
|
|
*/
|
|
affirm(tc->bit_depth == 16U && tr->tr.order == PNG_TR_GAMMA_ENCODE);
|
|
|
|
/* If the target bit depth is 8-bit delay the operation and use the
|
|
* standard 16-8-bit scale code. For low bit depth do it now.
|
|
*/
|
|
if (tr->to_bit_depth == 8U)
|
|
{
|
|
png_set_scale_16(png_ptr);
|
|
tr->tr.fn = NULL;
|
|
}
|
|
|
|
else /* low bit depth */
|
|
{
|
|
(void)init_gamma_sBIT(tr, tc);
|
|
tr->tr.fn = png_do_scale16_up;
|
|
tc->bit_depth = tr->to_bit_depth;
|
|
}
|
|
}
|
|
|
|
else /* gamma !significant and nothing to do */
|
|
tr->tr.fn = NULL;
|
|
}
|
|
}
|
|
|
|
static png_fixed_point
|
|
translate_gamma_flags(png_const_structrp png_ptr, png_fixed_point gamma,
|
|
int is_screen)
|
|
/* If 'is_screen' is set this returns the inverse of the supplied value; i.e.
|
|
* this routine always returns an encoding value.
|
|
*/
|
|
{
|
|
/* Check for flag values. The main reason for having the old Mac value as a
|
|
* flag is that it is pretty near impossible to work out what the correct
|
|
* value is from Apple documentation - a working Mac system is needed to
|
|
* discover the value!
|
|
*/
|
|
switch (gamma)
|
|
{
|
|
case PNG_DEFAULT_sRGB:
|
|
case PNG_GAMMA_sRGB:
|
|
case PNG_FP_1/PNG_GAMMA_sRGB: /* stupid case: -100000 */
|
|
gamma = PNG_GAMMA_sRGB_INVERSE;
|
|
break;
|
|
|
|
case PNG_GAMMA_MAC_18:
|
|
case PNG_FP_1/PNG_GAMMA_MAC_18: /* stupid case: -50000 */
|
|
gamma = PNG_GAMMA_MAC_INVERSE;
|
|
break;
|
|
|
|
default:
|
|
if (is_screen)
|
|
{
|
|
/* Check for a ridiculously low value; this will result in an
|
|
* overflow
|
|
* in the reciprocal calculation.
|
|
*/
|
|
if (gamma < 5)
|
|
{
|
|
png_app_error(png_ptr, "invalid screen gamma (too low)");
|
|
gamma = 0;
|
|
}
|
|
|
|
else if (gamma != PNG_FP_1) /* optimize linear */
|
|
gamma = png_reciprocal(gamma);
|
|
}
|
|
|
|
else if (gamma <= 0)
|
|
{
|
|
png_app_error(png_ptr, "invalid file gamma (too low)");
|
|
gamma = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return gamma;
|
|
}
|
|
|
|
static png_transform_gamma *
|
|
add_gamma_transform(png_structrp png_ptr, unsigned int order,
|
|
png_fixed_point gamma, unsigned int bit_depth, int force)
|
|
{
|
|
/* Add a png_transform_gamma transform at the given position; this is a
|
|
* utility which just adds the transform and (unconditionally) overwrites the
|
|
* to_gamma field. gamma must be valid. If 'force' is true the gamma value
|
|
* in an existing transform will be overwritten, otherwise this is just a
|
|
* default value.
|
|
*/
|
|
png_transform_gamma *tr = png_transform_cast(png_transform_gamma,
|
|
png_add_transform(png_ptr, sizeof (png_transform_gamma), png_init_gamma,
|
|
order));
|
|
|
|
if (force || tr->to_gamma == 0)
|
|
tr->to_gamma = gamma;
|
|
|
|
tr->to_bit_depth = bit_depth;
|
|
|
|
return tr;
|
|
}
|
|
|
|
void PNGFAPI
|
|
png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma,
|
|
png_fixed_point file_gamma)
|
|
{
|
|
png_debug(1, "in png_set_gamma_fixed");
|
|
|
|
/* Validate the passed in file gamma value: */
|
|
file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/);
|
|
|
|
/* The returned value may be 0, this results in a png_app_error above which
|
|
* may be ignored; if that happens simply ignore the setting.
|
|
*/
|
|
if (file_gamma > 0)
|
|
{
|
|
/* Set the colorspace gamma value unconditionally - this overrides the
|
|
* value in the PNG file if a gAMA chunk was present. png_set_alpha_mode
|
|
* provides a different, easier, way to default the file gamma.
|
|
*/
|
|
png_ptr->colorspace.gamma = file_gamma;
|
|
if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)
|
|
png_ptr->colorspace.flags = PNG_COLORSPACE_HAVE_GAMMA;
|
|
else
|
|
png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
|
|
}
|
|
|
|
/* Do the same thing with the screen gamma; check it and handle it if valid.
|
|
* This adds/sets the encoding of the final gamma transform in the chain.
|
|
* png_set_alpha_mode does the same thing.
|
|
*/
|
|
scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/);
|
|
|
|
if (scrn_gamma > 0)
|
|
(void)add_gamma_transform(png_ptr, PNG_TR_GAMMA_ENCODE, scrn_gamma,
|
|
0/*bit depth*/, 1/*force to_gamma to scrn_gamma*/);
|
|
}
|
|
|
|
#ifdef PNG_FLOATING_POINT_SUPPORTED
|
|
static png_fixed_point
|
|
convert_gamma_value(png_structrp png_ptr, double output_gamma)
|
|
{
|
|
/* The following silently ignores cases where fixed point (times 100,000)
|
|
* gamma values are passed to the floating point API. This is safe and it
|
|
* means the fixed point constants work just fine with the floating point
|
|
* API. The alternative would just lead to undetected errors and spurious
|
|
* bug reports. Negative values fail inside the _fixed API unless they
|
|
* correspond to the flag values.
|
|
*/
|
|
if (output_gamma < 0 || output_gamma > 128)
|
|
output_gamma *= .00001;
|
|
|
|
return png_fixed(png_ptr, output_gamma, "gamma value");
|
|
}
|
|
|
|
void PNGAPI
|
|
png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma)
|
|
{
|
|
png_set_gamma_fixed(png_ptr, convert_gamma_value(png_ptr, scrn_gamma),
|
|
convert_gamma_value(png_ptr, file_gamma));
|
|
}
|
|
#endif /* FLOATING_POINT */
|
|
#endif /* READ_GAMMA */
|
|
|
|
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
|
|
static void
|
|
png_do_rtog_48(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
const png_uint_32 r = (*transform)->args >> 16;
|
|
const png_uint_32 g = (*transform)->args & 0xFFFFU;
|
|
const png_uint_32 b = 65536U - r - g;
|
|
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 6U;
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
|
|
debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_FLAG_COLOR &&
|
|
(tc->gamma == 0U || !png_gamma_significant(png_ptr, tc->gamma, 16U)));
|
|
|
|
tc->sp = dp;
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
|
|
|
|
while (sp <= ep)
|
|
{
|
|
png_uint_32 gray = (((sp[0] << 8) + sp[1]) * r +
|
|
((sp[2] << 8) + sp[3]) * g +
|
|
((sp[4] << 8) + sp[5]) * b + 32767U) >> 16;
|
|
|
|
debug(gray < 65536U);
|
|
*dp++ = PNG_BYTE(gray >> 8);
|
|
*dp++ = PNG_BYTE(gray);
|
|
sp += 6U;
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_rtog_64(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
const png_uint_32 r = (*transform)->args >> 16;
|
|
const png_uint_32 g = (*transform)->args & 0xFFFFU;
|
|
const png_uint_32 b = 65536U - r - g;
|
|
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 8U;
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
|
|
debug(tc->bit_depth == 16U &&
|
|
tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA &&
|
|
(tc->gamma == 0U || !png_gamma_significant(png_ptr, tc->gamma, 16U)));
|
|
|
|
tc->sp = dp;
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
|
|
|
|
while (sp <= ep)
|
|
{
|
|
png_uint_32 gray = (((sp[0] << 8) + sp[1]) * r +
|
|
((sp[2] << 8) + sp[3]) * g +
|
|
((sp[4] << 8) + sp[5]) * b + 32767U) >> 16;
|
|
|
|
debug(gray < 65536U);
|
|
*dp++ = PNG_BYTE(gray >> 8);
|
|
*dp++ = PNG_BYTE(gray);
|
|
sp += 6U;
|
|
*dp++ = *sp++; /* alpha */
|
|
*dp++ = *sp++;
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_init_rgb_to_gray_arithmetic(png_transformp *transform,
|
|
png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
/* This only gets used in the final init stage: */
|
|
debug(tc->init == PNG_TC_INIT_FINAL && tc->bit_depth == 16U &&
|
|
(tc->format & PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA)) ==
|
|
PNG_FORMAT_FLAG_COLOR);
|
|
|
|
(*transform)->fn = (tc->format & PNG_FORMAT_FLAG_ALPHA) ? png_do_rtog_64 :
|
|
png_do_rtog_48;
|
|
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
|
|
tc->invalid_info |= PNG_INFO_sBIT;
|
|
tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
|
|
png_check_byte(png_ptr, tc->bit_depth);
|
|
# undef png_ptr
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
png_transform tr;
|
|
png_fixed_point red_coefficient;
|
|
png_fixed_point green_coefficient;
|
|
unsigned int coefficients_set :1;
|
|
unsigned int error_action :2;
|
|
} png_transform_rgb_to_gray;
|
|
|
|
static void
|
|
png_update_rgb_status(png_structrp png_ptr, png_transformp *transform)
|
|
{
|
|
png_transform_rgb_to_gray *tr = png_transform_cast(png_transform_rgb_to_gray,
|
|
*transform);
|
|
|
|
png_ptr->rgb_to_gray_status = 1U;
|
|
tr->tr.fn = NULL; /* one warning/error only */
|
|
|
|
switch (tr->error_action)
|
|
{
|
|
case PNG_ERROR_ACTION_WARN:
|
|
png_warning(png_ptr, "RGB to gray found nongray pixel");
|
|
break;
|
|
|
|
case PNG_ERROR_ACTION_ERROR:
|
|
png_error(png_ptr, "RGB to gray found nongray pixel");
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
png_do_rgb_check24(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
/* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue
|
|
* channels are not equal.
|
|
*/
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U;
|
|
|
|
debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_FLAG_COLOR);
|
|
|
|
while (sp <= ep)
|
|
{
|
|
if ((sp[0] ^ sp[1]) | (sp[2] ^ sp[1]))
|
|
{
|
|
png_update_rgb_status(png_ptr, transform);
|
|
break;
|
|
}
|
|
|
|
sp += 3U;
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_rgb_check32(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
/* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue
|
|
* channels are not equal and alpha is not zero.
|
|
*/
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 4U;
|
|
|
|
debug(tc->bit_depth == 8U &&
|
|
tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA);
|
|
|
|
while (sp <= ep)
|
|
{
|
|
if (((sp[0] ^ sp[1]) | (sp[2] ^ sp[1])) && sp[3] != 0)
|
|
{
|
|
png_update_rgb_status(png_ptr, transform);
|
|
break;
|
|
}
|
|
|
|
sp += 4U;
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_rgb_check48(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
/* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue
|
|
* channels are not equal.
|
|
*/
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 6U;
|
|
|
|
debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_FLAG_COLOR);
|
|
|
|
while (sp <= ep)
|
|
{
|
|
if ((sp[0] ^ sp[2]) | (sp[4] ^ sp[2]) |
|
|
(sp[1] ^ sp[3]) | (sp[5] ^ sp[3]))
|
|
{
|
|
png_update_rgb_status(png_ptr, transform);
|
|
break;
|
|
}
|
|
|
|
sp += 6U;
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_rgb_check64(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
/* Sets 'rgb_to_gray' status if a pixel is found where the red green and blue
|
|
* channels are not equal and alpha is not zero.
|
|
*/
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 8U;
|
|
|
|
debug(tc->bit_depth == 16U &&
|
|
tc->format == PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA);
|
|
|
|
while (sp <= ep)
|
|
{
|
|
if (((sp[0] ^ sp[2]) | (sp[4] ^ sp[2]) |
|
|
(sp[1] ^ sp[3]) | (sp[5] ^ sp[3])) &&
|
|
(sp[6] | sp[7]) != 0)
|
|
{
|
|
png_update_rgb_status(png_ptr, transform);
|
|
break;
|
|
}
|
|
|
|
sp += 8U;
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_init_rgb_to_gray(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
png_structrp png_ptr = tc->png_ptr;
|
|
|
|
/* Basic checks: if there is no color in the format this transform is not
|
|
* applicable.
|
|
*/
|
|
if ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0)
|
|
{
|
|
png_transform_rgb_to_gray *tr = png_transform_cast(
|
|
png_transform_rgb_to_gray, *transform);
|
|
|
|
/* no colormap allowed: */
|
|
affirm(tc->init && !(tc->format & PNG_FORMAT_FLAG_COLORMAP));
|
|
/* no extra flags yet: */
|
|
debug(!(tc->format &
|
|
PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA)));
|
|
/* at present no non-palette caching: */
|
|
implies(tc->caching, tc->palette);
|
|
|
|
if (tc->init == PNG_TC_INIT_FORMAT)
|
|
{
|
|
/* The convertion should just remove the 'COLOR' flag and do nothing
|
|
* else, but if a tRNS chunk is present this would invalidate it.
|
|
* Handle this by expanding it now.
|
|
*/
|
|
if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette &&
|
|
png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS))
|
|
{
|
|
/* Only if expand was requested and not cancelled: */
|
|
if (tc->expand_tRNS && !tc->strip_alpha)
|
|
tc->format |= PNG_FORMAT_FLAG_ALPHA;
|
|
|
|
tc->invalid_info |= PNG_INFO_tRNS; /* prevent expansion later */
|
|
}
|
|
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
|
|
}
|
|
|
|
else /* PNG_TC_INIT_FINAL */
|
|
{
|
|
unsigned int index; /* channel to select (invalid) */
|
|
png_byte sBIT_color; /* sBIT of that channel if valid */
|
|
png_fixed_point r, g; /* Coefficients in range 0..65536 */
|
|
|
|
/* Push a tRNS transform if required. Because this is a push the
|
|
* transform the init needs to be run now. This needs to go in
|
|
* before the check on r==g==b because a color key might be used.
|
|
*/
|
|
if ((tc->format & PNG_FORMAT_FLAG_ALPHA) == 0 && !tc->palette &&
|
|
png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS))
|
|
{
|
|
if (tc->expand_tRNS && !tc->strip_alpha)
|
|
{
|
|
png_transformp tr_expand = png_push_transform(png_ptr,
|
|
sizeof (png_expand), png_init_expand, transform, NULL);
|
|
|
|
debug(*transform == tr_expand);
|
|
tr_expand->args |= PNG_EXPAND_tRNS;
|
|
png_init_expand(transform, tc);
|
|
/* Check for the infinite loop possibility: */
|
|
affirm((tc->invalid_info & PNG_INFO_tRNS) != 0);
|
|
return;
|
|
}
|
|
|
|
else
|
|
tc->invalid_info |= PNG_INFO_tRNS;
|
|
}
|
|
|
|
{
|
|
png_fixed_point red, green;
|
|
|
|
if (tr->coefficients_set)
|
|
{
|
|
red = tr->red_coefficient;
|
|
green = tr->green_coefficient;
|
|
}
|
|
|
|
# ifdef PNG_COLORSPACE_SUPPORTED
|
|
else if ((png_ptr->colorspace.flags &
|
|
(PNG_COLORSPACE_HAVE_ENDPOINTS+PNG_COLORSPACE_INVALID))
|
|
== PNG_COLORSPACE_HAVE_ENDPOINTS)
|
|
{
|
|
red = png_ptr->colorspace.end_points_XYZ.red_Y;
|
|
green = png_ptr->colorspace.end_points_XYZ.green_Y;
|
|
}
|
|
# endif
|
|
|
|
else /* no colorspace support, assume sRGB */
|
|
{
|
|
/* From IEC 61966-2-1:1999, the reverse transformation from sRGB
|
|
* RGB values to XYZ D65 values (not CIEXYZ!). These are not
|
|
* exact inverses of the forward transformation; they only have
|
|
* four (decimal) digits of precision.
|
|
*
|
|
* API CHANGE: in 1.7.0 the sRGB values from the official IEC
|
|
* specification are used, previously libpng used values from
|
|
* Charles Poynton's ColorFAQ of 1998-01-04. The original page
|
|
* is gone, however up to date information can be found below:
|
|
*
|
|
* http://www.poynton.com/ColorFAQ.html
|
|
*
|
|
* At the time of reading (20150628) this web site quotes the
|
|
* same values as below and cites ITU Rec 709 as the source.
|
|
*/
|
|
red = 21260;
|
|
green = 71520;
|
|
}
|
|
|
|
/* Prior to 1.7 this calculation was done with 15-bit precision,
|
|
* this is because the code was written pre-muldiv and tried to
|
|
* work round the problems caused by the signs in integer
|
|
* calculations.
|
|
*/
|
|
(void)png_muldiv(&r, red, 65536, PNG_FP_1);
|
|
(void)png_muldiv(&g, green, 65536, PNG_FP_1);
|
|
}
|
|
|
|
/* If the convertion can be deduced to select a single channel do so.
|
|
* If the error action is set to error just copy the red channel, if
|
|
* the coefficients select just one channel use that.
|
|
*/
|
|
if (tr->error_action == PNG_ERROR_ACTION_ERROR || r >= 65536)
|
|
index = 0U, sBIT_color = tc->sBIT_R; /* select red */
|
|
|
|
else if (g >= 65536)
|
|
index = 1U, sBIT_color = tc->sBIT_G; /* select green */
|
|
|
|
else if (r + g == 0)
|
|
index = 2U, sBIT_color = tc->sBIT_B; /* select blue */
|
|
|
|
else
|
|
index = 3U, sBIT_color = 0U/*UNUSED*/;
|
|
|
|
if (index == 3U)
|
|
{
|
|
/* Arithmetic will have to be done. For this we need linear 16-bit
|
|
* data which must then be converted back to the required bit depth,
|
|
* png_init_gamma handles this. It may push other expand operations
|
|
* (it shouldn't but it can), so give it some space.
|
|
*
|
|
* The gamma must be restored to the original value, 0U for the bit
|
|
* depth means use the output bit depth.
|
|
*/
|
|
(void)add_gamma_transform(png_ptr, PNG_TR_GAMMA_ENCODE, tc->gamma,
|
|
0U/*bit depth*/, 0/*default*/);
|
|
|
|
/* If png_init_gamma is called with tc->gamma 0 it does the right
|
|
* thing in PNG_TC_INIT_FINAL; it just does any required bit depth
|
|
* adjustment.
|
|
*/
|
|
(void)add_gamma_transform(png_ptr, tr->tr.order + 0x10U, PNG_FP_1,
|
|
16U, 1/*force: doesn't matter*/);
|
|
|
|
{
|
|
/* This init routine will update the sBIT information
|
|
* appropriately.
|
|
*/
|
|
png_transformp tr_rtog = png_add_transform(png_ptr, 0/*size*/,
|
|
png_init_rgb_to_gray_arithmetic, tr->tr.order + 0x20U);
|
|
|
|
/* r and g are known to be in the range 0..65535, so pack them
|
|
* into the 'args' argument of a new transform.
|
|
*/
|
|
tr_rtog->args = (((png_uint_32)r) << 16) + g;
|
|
}
|
|
}
|
|
|
|
else /* index < 3 */
|
|
{
|
|
/* TODO: does this need to select the correct sBIT value too? */
|
|
png_add_rgb_to_gray_byte_ops(png_ptr, tc, index,
|
|
tr->tr.order + 0x10U);
|
|
tc->sBIT_G = sBIT_color;
|
|
}
|
|
|
|
/* Prior to 1.7 libpng would always check for r!=g!=b. In 1.7 an extra
|
|
* error_action setting is added to prevent this overhead.
|
|
*/
|
|
if (tr->error_action)
|
|
tr->tr.fn = tc->bit_depth == 8 ?
|
|
((tc->format & PNG_FORMAT_FLAG_ALPHA) ?
|
|
png_do_rgb_check32 : png_do_rgb_check24) :
|
|
((tc->format & PNG_FORMAT_FLAG_ALPHA) ?
|
|
png_do_rgb_check64 : png_do_rgb_check48);
|
|
|
|
else
|
|
tr->tr.fn = NULL; /* PNG_ERROR_ACTION_NO_CHECK */
|
|
}
|
|
}
|
|
|
|
else /* not color: transform not applicable */
|
|
(*transform)->fn = NULL;
|
|
}
|
|
|
|
void PNGFAPI
|
|
png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action,
|
|
png_fixed_point red, png_fixed_point green)
|
|
/* API CHANGE: in 1.7 calling this on a palette PNG no longer causes the
|
|
* palette to be expanded (unless explicitly requested), rather it converts
|
|
* the palette to grayscale.
|
|
*/
|
|
{
|
|
/* The coefficients must be reasonable, the error handling is to warn (pre
|
|
* 1.7) or app error (1.7) and drop back to the cHRM definition of Y. The
|
|
* drop back is done in the init routine if relevant flag is unset. Passing
|
|
* negative values causes this default to be used without a warning.
|
|
*/
|
|
int pset = 0;
|
|
|
|
if (red >= 0 && green >= 0)
|
|
{
|
|
if (red <= PNG_FP_1 && green <= PNG_FP_1 && red + green <= PNG_FP_1)
|
|
pset = 1;
|
|
|
|
else /* overflow */
|
|
png_app_error(png_ptr, "rgb_to_gray coefficients too large (ignored)");
|
|
}
|
|
|
|
{
|
|
png_transform_rgb_to_gray *tr =
|
|
png_transform_cast(png_transform_rgb_to_gray,
|
|
png_add_transform(png_ptr, sizeof (png_transform_rgb_to_gray),
|
|
png_init_rgb_to_gray, PNG_TR_RGB_TO_GRAY));
|
|
|
|
tr->error_action = 0x3U & error_action;
|
|
|
|
if (red < 0 || green < 0) /* use cHRM default */
|
|
tr->coefficients_set = 0U;
|
|
|
|
else if (pset) /* else bad coefficients which get ignored */
|
|
{
|
|
tr->coefficients_set = 1U;
|
|
tr->red_coefficient = red;
|
|
tr->green_coefficient = green;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef PNG_FLOATING_POINT_SUPPORTED
|
|
/* Convert a RGB image to a grayscale of the same width. This allows us,
|
|
* for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image.
|
|
*/
|
|
|
|
void PNGAPI
|
|
png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red,
|
|
double green)
|
|
{
|
|
png_set_rgb_to_gray_fixed(png_ptr, error_action,
|
|
png_fixed(png_ptr, red, "rgb to gray red coefficient"),
|
|
png_fixed(png_ptr, green, "rgb to gray green coefficient"));
|
|
}
|
|
#endif /* FLOATING POINT */
|
|
#endif /* RGB_TO_GRAY */
|
|
|
|
#ifdef PNG_READ_BACKGROUND_SUPPORTED
|
|
typedef struct
|
|
{
|
|
png_transform tr;
|
|
struct
|
|
{
|
|
png_color_16 background;
|
|
unsigned int need_expand :1; /* Background matches format of PNG */
|
|
unsigned int rgb_to_gray :1; /* RGB-to-gray transform found */
|
|
unsigned int compose_background :1; /* png_set_background */
|
|
unsigned int associate_alpha :1;
|
|
unsigned int encode_alpha :1;
|
|
unsigned int optimize_alpha :1;
|
|
unsigned int background_is_gray :1; /* Background color is gray */
|
|
unsigned int background_bit_depth :5; /* bit depth, 1..16 */
|
|
unsigned int ntrans :3; /* 1..6 bytes */
|
|
png_byte transparent_pixel[6];
|
|
png_byte background_pixel[6];
|
|
png_fixed_point background_gamma;
|
|
} st; /* to allow the whole state to be copied reliably */
|
|
} png_transform_background;
|
|
|
|
static void
|
|
resolve_background_color(png_transform_background *tr,
|
|
png_transform_controlp tc)
|
|
{
|
|
png_const_structp png_ptr = tc->png_ptr;
|
|
|
|
/* Deduce the bit depth and color information for the background, the
|
|
* special case is when need_expand is set and the PNG has palette format,
|
|
* then (and only then) the background value is a palette index.
|
|
*/
|
|
if (tr->st.need_expand && tc->palette)
|
|
{
|
|
unsigned int i = tr->st.background.index;
|
|
png_byte r, g, b;
|
|
|
|
if (i >= png_ptr->num_palette)
|
|
{
|
|
png_app_error(png_ptr, "background index out of range");
|
|
tr->tr.fn = NULL;
|
|
return;
|
|
}
|
|
|
|
tr->st.background_bit_depth = 8U;
|
|
r = png_ptr->palette[i].red;
|
|
g = png_ptr->palette[i].green;
|
|
b = png_ptr->palette[i].blue;
|
|
|
|
if (r == g && g == b)
|
|
{
|
|
tr->st.background_is_gray = 1U;
|
|
tr->st.background.gray = g;
|
|
UNTESTED
|
|
}
|
|
|
|
else
|
|
{
|
|
tr->st.background_is_gray = 0U;
|
|
tr->st.background.red = r;
|
|
tr->st.background.green = g;
|
|
tr->st.background.blue = b;
|
|
UNTESTED
|
|
}
|
|
}
|
|
|
|
else /* background is not a palette index */
|
|
{
|
|
int use_rgb;
|
|
png_uint_16 mask;
|
|
|
|
/* First work out the bit depth and whether or not to use the RGB
|
|
* fields of the background.
|
|
*/
|
|
if (tr->st.need_expand)
|
|
{
|
|
affirm(!(tc->format & PNG_FORMAT_FLAG_COLORMAP));
|
|
tr->st.background_bit_depth =
|
|
png_check_bits(png_ptr, png_ptr->bit_depth, 5U);
|
|
use_rgb = (png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0;
|
|
}
|
|
|
|
else /* screen format background */
|
|
{
|
|
/* If the final output is in palette format assume the background
|
|
* is in a matching format. This covers two cases, an original
|
|
* COLORMAP PNG and png_set_quantize.
|
|
*/
|
|
if ((png_ptr->row_format & PNG_FORMAT_FLAG_COLORMAP) != 0)
|
|
tr->st.background_bit_depth = 8U;
|
|
|
|
else
|
|
tr->st.background_bit_depth =
|
|
png_check_bits(png_ptr, png_ptr->row_bit_depth, 5U);
|
|
|
|
use_rgb = (png_ptr->row_format & PNG_FORMAT_FLAG_COLOR) != 0;
|
|
}
|
|
|
|
/* The PNG spec says to use the low bits of the values, so we mask out
|
|
* the high bits here (at present no warning is produced if they are
|
|
* set.)
|
|
*/
|
|
mask = png_check_u16(png_ptr, (1U << tr->st.background_bit_depth)-1U);
|
|
|
|
if (use_rgb)
|
|
{
|
|
png_uint_16 r, g, b;
|
|
|
|
r = tr->st.background.red & mask;
|
|
g = tr->st.background.green & mask;
|
|
b = tr->st.background.blue & mask;
|
|
|
|
if (r == g && g == b)
|
|
{
|
|
tr->st.background_is_gray = 1U;
|
|
tr->st.background.gray = g;
|
|
}
|
|
|
|
else
|
|
{
|
|
tr->st.background_is_gray = 0U;
|
|
tr->st.background.red = r;
|
|
tr->st.background.green = g;
|
|
tr->st.background.blue = b;
|
|
}
|
|
}
|
|
|
|
else /* gray */
|
|
{
|
|
tr->st.background_is_gray = 1U;
|
|
tr->st.background.gray = tr->st.background.gray & mask;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gamma_correct_background_component(png_const_structrp png_ptr, png_uint_16p cp,
|
|
unsigned int bdc, png_fixed_point correction, unsigned int bdout)
|
|
/* Utility function for gamma_correct_background. */
|
|
{
|
|
unsigned int c = *cp;
|
|
|
|
/* 0.0 and 1.0 are unchanged (and common): */
|
|
if (c > 0U && c < (1U<<bdc)-1U)
|
|
{
|
|
if (correction != 0)
|
|
c = png_check_bits(png_ptr,
|
|
png_gamma_nxmbit_correct(c, correction, bdc, bdout), bdout);
|
|
|
|
else if (bdc != bdout)
|
|
{
|
|
/* Scale the value from bdc to bdout bits. */
|
|
png_int_32 i;
|
|
affirm(png_muldiv(&i, c, (1U<<bdout)-1U, (1U<<bdc)-1U));
|
|
c = png_check_bits(png_ptr, i, bdout);
|
|
}
|
|
}
|
|
|
|
else if (c != 0U)
|
|
c = (1U << bdout) - 1U;
|
|
|
|
*cp = PNG_UINT_16(c);
|
|
PNG_UNUSED(png_ptr) /* if checking disabled */
|
|
}
|
|
|
|
static void
|
|
gamma_correct_background(png_transform_background *tr,
|
|
png_const_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_fixed_point correction = tc->gamma;
|
|
const unsigned int bdback = tr->st.background_bit_depth;
|
|
const unsigned int bdrow = tc->bit_depth;
|
|
|
|
/* This is harmless if it fails but it will damage the output pixels - they
|
|
* won't have the requested color depth accuracy where the background is
|
|
* used.
|
|
*/
|
|
debug(bdback <= bdrow);
|
|
debug(tr->st.background_is_gray || (bdrow >= 8U && bdback >= 8U));
|
|
|
|
/* The background is assumed to be full precision; there is no sBIT
|
|
* information for it. The convertion converts from the current depth and
|
|
* gamma of the background to that in the transform control. It uses the
|
|
* full 16-bit precision when considering the gamma values even though this
|
|
* is probably spurious.
|
|
*/
|
|
if (correction != 0 && (tr->st.background_gamma == 0 ||
|
|
png_gamma_equal(png_ptr, tr->st.background_gamma, correction,
|
|
&correction, 16U)))
|
|
correction = 0; /* no correction! */
|
|
|
|
if (tr->st.background_is_gray)
|
|
gamma_correct_background_component(png_ptr, &tr->st.background.gray,
|
|
bdback, correction, bdrow);
|
|
|
|
else
|
|
{
|
|
gamma_correct_background_component(png_ptr, &tr->st.background.red,
|
|
bdback, correction, bdrow);
|
|
gamma_correct_background_component(png_ptr, &tr->st.background.green,
|
|
bdback, correction, bdrow);
|
|
gamma_correct_background_component(png_ptr, &tr->st.background.blue,
|
|
bdback, correction, bdrow);
|
|
}
|
|
|
|
/* Regardless of whether there was a correction set the background gamma: */
|
|
tr->st.background_gamma = tc->gamma;
|
|
tr->st.background_bit_depth = png_check_bits(png_ptr, bdrow, 5U);
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
fill_background_pixel(png_transform_background *tr, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
/* Fill in 'background_pixel' if the appropriate sequence of bytes for the
|
|
* format given in the transform control.
|
|
*/
|
|
unsigned int bdtc = tc->bit_depth;
|
|
|
|
/* If necessary adjust the background pixel to the current row format (it is
|
|
* important to do this as late as possible to avoid spurious
|
|
* interconvertions).
|
|
*/
|
|
gamma_correct_background(tr, tc);
|
|
|
|
if (tr->st.background_is_gray)
|
|
{
|
|
unsigned int g = tr->st.background.gray;
|
|
|
|
/* 'g' now has enough bits for the destination, note that in the case of
|
|
* low bit depth gray this causes the pixel to be replicated through the
|
|
* written byte. Fill all six bytes with the replicated background:
|
|
*/
|
|
while (bdtc < 8U)
|
|
{
|
|
g &= (1U << bdtc) - 1U; /* use only the low bits */
|
|
g |= g << bdtc;
|
|
bdtc <<= 1;
|
|
}
|
|
|
|
memset(tr->st.background_pixel, PNG_BYTE(g), 6U);
|
|
if (bdtc == 16U)
|
|
tr->st.background_pixel[0] = tr->st.background_pixel[2] =
|
|
tr->st.background_pixel[4] = PNG_BYTE(g >> 8);
|
|
/* Must not include the alpha channel here: */
|
|
tr->st.ntrans = png_check_bits(png_ptr,
|
|
((tc->format & PNG_FORMAT_FLAG_COLOR)+1U) << (bdtc == 16U), 3U);
|
|
}
|
|
|
|
else
|
|
{
|
|
unsigned int r = tr->st.background.red;
|
|
unsigned int g = tr->st.background.green;
|
|
unsigned int b = tr->st.background.blue;
|
|
|
|
debug((tc->format & PNG_FORMAT_FLAG_COLOR) != 0);
|
|
|
|
switch (bdtc)
|
|
{
|
|
case 8U:
|
|
tr->st.background_pixel[0] = PNG_BYTE(r);
|
|
tr->st.background_pixel[1] = PNG_BYTE(g);
|
|
tr->st.background_pixel[2] = PNG_BYTE(b);
|
|
tr->st.ntrans = 3U;
|
|
break;
|
|
|
|
case 16U:
|
|
tr->st.background_pixel[0] = PNG_BYTE(r>>8);
|
|
tr->st.background_pixel[1] = PNG_BYTE(r);
|
|
tr->st.background_pixel[2] = PNG_BYTE(g>>8);
|
|
tr->st.background_pixel[3] = PNG_BYTE(g);
|
|
tr->st.background_pixel[4] = PNG_BYTE(b>>8);
|
|
tr->st.background_pixel[5] = PNG_BYTE(b);
|
|
tr->st.ntrans = 6U;
|
|
break;
|
|
|
|
default:
|
|
NOT_REACHED;
|
|
}
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
/* Look for colors matching the trans_color in png_ptr and replace them. This
|
|
* must handle all the non-alpha formats.
|
|
*/
|
|
static void
|
|
png_do_replace_tRNS_multi(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
const unsigned int cbytes = tr->st.ntrans;
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - cbytes/*safety*/;
|
|
const int copy = dp != sp;
|
|
|
|
/* We expect opaque and transparent pixels to be interleaved but with long
|
|
* sequences of each.
|
|
*/
|
|
debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) &&
|
|
PNG_TC_PIXEL_DEPTH(*tc) == cbytes << 3);
|
|
tc->invalid_info |= PNG_INFO_tRNS;
|
|
tc->sp = dp;
|
|
|
|
/* Look for pixels that match the transparent value, copying opaque ones as
|
|
* required.
|
|
*/
|
|
do
|
|
{
|
|
const png_const_bytep opaque_start = sp;
|
|
size_t cb;
|
|
|
|
/* Find a transparent pixel, or the end: */
|
|
do
|
|
{
|
|
if (memcmp(sp, tr->st.transparent_pixel, cbytes) == 0) /*transparent*/
|
|
break;
|
|
sp += cbytes;
|
|
}
|
|
while (sp <= ep);
|
|
|
|
cb = sp - opaque_start;
|
|
|
|
/* Copy any opaque pixels: */
|
|
if (cb > 0)
|
|
{
|
|
if (copy)
|
|
memcpy(dp, opaque_start, cb);
|
|
dp += cb;
|
|
}
|
|
|
|
/* Set transparent pixels to the background (this has to be done one-by
|
|
* one; the case where all the bytes in the background are equal is not
|
|
* optimized.)
|
|
*/
|
|
if (sp <= ep) do
|
|
{
|
|
memcpy(dp, tr->st.background_pixel, cbytes);
|
|
sp += cbytes;
|
|
dp += cbytes;
|
|
}
|
|
while (sp <= ep && memcmp(sp, tr->st.transparent_pixel, cbytes) == 0);
|
|
} while (sp <= ep);
|
|
|
|
debug(sp == ep+cbytes);
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_replace_tRNS_8(png_transformp *transform, png_transform_controlp tc)
|
|
/* The single byte version: 8-bit gray */
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_alloc_size_t row_bytes = tc->width;
|
|
const int copy = dp != sp;
|
|
const int transparent_pixel = tr->st.transparent_pixel[0];
|
|
const int background_pixel = tr->st.background_pixel[0];
|
|
|
|
/* We expect opaque and transparent pixels to be interleaved but with long
|
|
* sequences of each.
|
|
*/
|
|
debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) &&
|
|
PNG_TC_PIXEL_DEPTH(*tc) == 8 && tr->st.ntrans == 1);
|
|
tc->invalid_info |= PNG_INFO_tRNS;
|
|
tc->sp = dp;
|
|
|
|
/* Now search for a byte that matches the transparent pixel. */
|
|
do
|
|
{
|
|
const png_const_bytep tp = png_voidcast(png_const_bytep,
|
|
memchr(sp, transparent_pixel, row_bytes));
|
|
png_alloc_size_t cb;
|
|
|
|
if (tp == NULL) /* all remaining pixels are opaque */
|
|
{
|
|
if (copy)
|
|
memcpy(dp, sp, row_bytes);
|
|
return;
|
|
}
|
|
|
|
cb = tp - sp;
|
|
if (cb > 0) /* some opaque pixels found */
|
|
{
|
|
if (copy)
|
|
memcpy(dp, sp, cb);
|
|
sp = tp;
|
|
dp += cb;
|
|
debug(row_bytes > cb);
|
|
row_bytes -= cb;
|
|
}
|
|
|
|
/* Now count the transparent pixels, this could use strspn but for the
|
|
* moment does not.
|
|
*/
|
|
debug(row_bytes > 0);
|
|
++sp; /* next to check, may be beyond the last */
|
|
while (--row_bytes > 0 && *sp == transparent_pixel) ++sp;
|
|
|
|
cb = sp - tp;
|
|
memset(dp, background_pixel, cb);
|
|
dp += cb;
|
|
} while (row_bytes > 0);
|
|
UNTESTED
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_set_row(png_transformp *transform, png_transform_controlp tc)
|
|
/* This is a no-op transform that both invalidates INFO from args and sets
|
|
* the entire row to the byte given in the top bits.
|
|
*/
|
|
{
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
|
|
tc->sp = dp;
|
|
memset(dp, (*transform)->args >> 24, PNG_TC_ROWBYTES(*tc));
|
|
}
|
|
|
|
static void
|
|
png_do_replace_tRNS_lbd(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
/* This is the 2 or 4 bit depth grayscale case; the 1 bit case is handled by
|
|
* the two routines above and the 8-bit and 16-bit cases by the two before
|
|
* that.
|
|
*
|
|
* The transform contains pixel values that have been expanded to one byte,
|
|
* the code needs to match the tRNS pixel and substitute the background one
|
|
* in each byte.
|
|
*/
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
|
|
const unsigned int copy = sp != dp;
|
|
const png_byte transparent_pixel = tr->st.transparent_pixel[0];
|
|
const png_byte background_pixel = tr->st.background_pixel[0];
|
|
|
|
/* We expect opaque and transparent pixels to be interleaved but with long
|
|
* sequences of each.
|
|
*/
|
|
debug(!(tc->format & PNG_FORMAT_FLAG_ALPHA) &&
|
|
PNG_TC_PIXEL_DEPTH(*tc) < 8 && tr->st.ntrans == 1);
|
|
tc->sp = dp;
|
|
|
|
/* Now search for a byte that contains the transparent pixel
|
|
*
|
|
* NOTE: this is the "strlen" algorithm, I first saw a variant implemented in
|
|
* Acorn RISC iX (strlen) around 1991, almost certainly derived from a
|
|
* suggestion by Alan Mycroft dating from April 27, 1987 (Mycroft was one of
|
|
* the authors of the 'Norcroft' compiler used for RISC iX, and well known to
|
|
* the RISC iX implementors.) See, e.g.:
|
|
*
|
|
* http://bits.stephan-brumme.com/null.html.
|
|
*
|
|
* The exact form used here is the one reported by Brumme; I haven't been
|
|
* able to find the original Mycroft posting, it was probably on comp.arch.
|
|
*
|
|
* The 4-bit and 2-bit versions (probably slower in the 4-bit case than the
|
|
* do-it-by-pixel version, but definately faster once 32-bit handling is
|
|
* implemented):
|
|
*
|
|
* 4 bit: (byte - 0x11) & ~byte & 0x88
|
|
* 2 bit: (byte - 0x55) & ~byte & 0xcc
|
|
*
|
|
* The generalizations to 32 bits (8 and 16 pixels per step) should be
|
|
* obvious.
|
|
*
|
|
* This algorithm reads pixels within a byte beyond the end of the row and,
|
|
* potentially, changes the non-existent pixels. This is harmless and not
|
|
* a security risk.
|
|
*/
|
|
if (tc->bit_depth == 4U)
|
|
{
|
|
/* For the moment the algorithm isn't used; there are only two pixels in
|
|
* each byte so it is likely to be quicker to check as below:
|
|
*/
|
|
do
|
|
{
|
|
const png_byte b = *sp++;
|
|
const unsigned int m = b ^ transparent_pixel;
|
|
|
|
if (m == 0U) /* both transparent */
|
|
*dp = background_pixel;
|
|
|
|
else if ((m & 0xF0U) == 0U) /* first transparent */
|
|
*dp = PNG_BYTE((background_pixel & 0xF0U) | (b & 0x0FU));
|
|
|
|
else if ((m & 0x0FU) == 0U) /* second transparent */
|
|
*dp = PNG_BYTE((background_pixel & 0x0FU) | (b & 0xF0U));
|
|
|
|
else if (copy) /* neither transparent */
|
|
*dp = b;
|
|
|
|
++dp;
|
|
} while (sp < ep);
|
|
}
|
|
|
|
else
|
|
{
|
|
affirm(tc->bit_depth == 2U);
|
|
|
|
do
|
|
{
|
|
const png_byte b = *sp++;
|
|
const unsigned int m = b ^ transparent_pixel;
|
|
|
|
if (m == 0U) /* transparent */
|
|
*dp = background_pixel;
|
|
|
|
else if (0xAAU & ((m - 0x55U) & ~m))
|
|
{
|
|
/* One or more pixels transparent */
|
|
const unsigned int mask =
|
|
(m & 0xC0U ? 0xC0U : 0U) |
|
|
(m & 0x30U ? 0x30U : 0U) |
|
|
(m & 0x0CU ? 0x0CU : 0U) |
|
|
(m & 0x03U ? 0x03U : 0U);
|
|
|
|
*dp = PNG_BYTE((b & mask) | (background_pixel & ~mask));
|
|
}
|
|
|
|
else if (copy) /* no transparent pixels */
|
|
*dp = b;
|
|
|
|
++dp;
|
|
} while (sp < ep);
|
|
}
|
|
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_background_with_transparent_GA8(png_transformp *transform,
|
|
png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 1U/*safety*/;
|
|
const png_byte background_pixel = tr->st.background_pixel[0];
|
|
|
|
/* Because this is an alpha format and we are removing the alpha channel we
|
|
* can copy up.
|
|
*/
|
|
debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_GA &&
|
|
tr->st.ntrans == 1U);
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
|
|
tc->sp = dp;
|
|
|
|
/* Look for pixels that have alpha 0; all others should have alpha 1.0,
|
|
* however they are simply treated as opaque regardless.
|
|
*/
|
|
do
|
|
{
|
|
*dp++ = (sp[1] == 0U) ? background_pixel : sp[0];
|
|
sp += 2U;
|
|
} while (sp < ep);
|
|
|
|
debug(sp == ep+1U);
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_background_with_transparent_GA16(png_transformp *transform,
|
|
png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/;
|
|
|
|
debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_GA &&
|
|
tr->st.ntrans == 2U);
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
|
|
tc->sp = dp;
|
|
|
|
do
|
|
{
|
|
if (sp[2] == 0U && sp[3] == 0U) /* transparent */
|
|
dp[0] = tr->st.background_pixel[0], dp[1] = tr->st.background_pixel[1];
|
|
|
|
else
|
|
dp[0] = sp[0], dp[1] = sp[1];
|
|
|
|
dp += 2U;
|
|
sp += 4U;
|
|
} while (sp < ep);
|
|
|
|
debug(sp == ep+3U);
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_background_with_transparent_GAlbd(png_transformp *transform,
|
|
png_transform_controlp tc)
|
|
/* This is the low-bit-depth gray case, the input is 1, 2 or 4-bit per
|
|
* channel gray-alpha.
|
|
*/
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
|
|
const unsigned int bit_depth = tc->bit_depth;
|
|
const unsigned int mask = (1U << bit_depth) - 1U;
|
|
const unsigned int back = tr->st.background_pixel[0] & mask;
|
|
unsigned int opos, ob, inb;
|
|
|
|
debug(bit_depth < 8U && tc->format == PNG_FORMAT_GA && tr->st.ntrans == 1U);
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
|
|
tc->sp = dp;
|
|
|
|
ob = 0U; /* output byte */
|
|
opos = 0U; /* bit index of previous output pixel (counts down) */
|
|
inb = 0U; /* quiet a GCC 4.8.5 warning */
|
|
|
|
for (;;)
|
|
{
|
|
/* The output is half the size of the input, so we need a new input byte
|
|
* for every 4 bits of output:
|
|
*/
|
|
if (opos == 0U || opos == 4U)
|
|
{
|
|
if (sp >= ep)
|
|
break;
|
|
|
|
inb = *sp++;
|
|
}
|
|
|
|
/* Move to the next *output* pixel, this wraps when bits is 0U: */
|
|
opos = (opos - bit_depth) & 0x7U;
|
|
|
|
/* Extract the whole input pixel to the low bits of a temporary: */
|
|
{
|
|
unsigned int pixel = inb >> ((opos*2U) & 0x7U);
|
|
|
|
/* The alpha channel is second, check for a value of 0: */
|
|
if ((pixel & mask)/* A component*/ == 0U)
|
|
pixel = back;
|
|
|
|
else
|
|
{
|
|
debug((pixel & mask) == mask);
|
|
pixel = (pixel >> bit_depth) & mask; /* G component */
|
|
}
|
|
|
|
ob |= pixel << opos;
|
|
}
|
|
|
|
if (opos == 0U)
|
|
*dp++ = PNG_BYTE(ob), ob = 0U;
|
|
}
|
|
|
|
if (opos != 0U)
|
|
*dp++ = PNG_BYTE(ob);
|
|
|
|
debug(sp == ep);
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_background_with_transparent_RGBA8(png_transformp *transform,
|
|
png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/;
|
|
|
|
debug(tc->bit_depth == 8U && tc->format == PNG_FORMAT_RGBA &&
|
|
tr->st.ntrans == 3U);
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
|
|
tc->sp = dp;
|
|
|
|
do
|
|
{
|
|
if (sp[3] == 0U) /* transparent */
|
|
memcpy(dp, tr->st.background_pixel, 3U);
|
|
|
|
else
|
|
memmove(dp, sp, 3U);
|
|
|
|
dp += 3U;
|
|
sp += 4U;
|
|
} while (sp < ep);
|
|
|
|
debug(sp == ep+3U);
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_background_with_transparent_RGBA16(png_transformp *transform,
|
|
png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 7U/*safety*/;
|
|
|
|
debug(tc->bit_depth == 16U && tc->format == PNG_FORMAT_RGBA &&
|
|
tr->st.ntrans == 6U);
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
|
|
tc->sp = dp;
|
|
|
|
do
|
|
{
|
|
if (sp[6] == 0U && sp[7] == 0U) /* transparent */
|
|
memcpy(dp, tr->st.background_pixel, 6U);
|
|
|
|
else
|
|
memmove(dp, sp, 6U);
|
|
|
|
dp += 6U;
|
|
sp += 8U;
|
|
} while (sp < ep);
|
|
|
|
debug(sp == ep+7U);
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_init_background_transparent(png_transformp *transform,
|
|
png_transform_controlp tc)
|
|
/* Select the correct version of the above routines. */
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
|
|
debug(tc->init == PNG_TC_INIT_FINAL /* never called in 'FORMAT' */ &&
|
|
(tc->format & PNG_FORMAT_FLAG_ALPHA) != 0);
|
|
|
|
/* Now we know the format on which processing will happen so it is possible
|
|
* to generate the correct fill pixel value to use.
|
|
*/
|
|
fill_background_pixel(tr, tc);
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
|
|
tc->invalid_info |= PNG_INFO_sBIT;
|
|
tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
|
|
png_check_byte(png_ptr, tc->bit_depth);
|
|
|
|
if (!(tc->format & PNG_FORMAT_FLAG_COLOR))
|
|
{
|
|
if (tc->bit_depth == 8U)
|
|
tr->tr.fn = png_do_background_with_transparent_GA8;
|
|
|
|
else if (tc->bit_depth == 16U)
|
|
tr->tr.fn = png_do_background_with_transparent_GA16;
|
|
|
|
else /* low-bit-depth gray with alpha (not a PNG format!) */
|
|
tr->tr.fn = png_do_background_with_transparent_GAlbd;
|
|
}
|
|
|
|
else /* color */
|
|
{
|
|
if (tc->bit_depth == 8U)
|
|
tr->tr.fn = png_do_background_with_transparent_RGBA8;
|
|
|
|
else
|
|
{
|
|
debug(tc->bit_depth == 16U);
|
|
tr->tr.fn = png_do_background_with_transparent_RGBA16;
|
|
}
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
/* The calculated values below have the range 0..65535*65535, the output has the
|
|
* range 0..65535, so divide by 65535. Two approaches are given here, one
|
|
* modifies the value in place, the other uses a more complex expression. With
|
|
* gcc on an AMD64 system the in-place approach is very slightly faster.
|
|
*
|
|
* The two expressions are slightly different in what they calculate but both
|
|
* give the exact answer (verified by exhaustive testing.)
|
|
*
|
|
* The macro must be given a png_uint_32 variable (lvalue), normally an auto
|
|
* variable.
|
|
*/
|
|
#ifndef PNG_COMPOSE_DIV_65535
|
|
# ifdef PNG_COMPOSE_DIV_EXPRESSION_SUPPORTED
|
|
# define PNG_COMPOSE_DIV_65535(v)\
|
|
(v = ((v + (v>>16) + (v>>31) + 32768U) >> 16))
|
|
# else
|
|
# define PNG_COMPOSE_DIV_65535(v)\
|
|
(v += v >> 16, v += v >> 31, v += 32768U, v >>= 16)
|
|
# endif
|
|
#endif
|
|
|
|
static void
|
|
png_do_background_alpha_GA(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 3U/*safety*/;
|
|
const unsigned int background = tr->st.background.gray;
|
|
const int copy = (sp != dp);
|
|
const int compose = tr->st.compose_background;
|
|
|
|
affirm(tc->bit_depth == 16U && tc->format == PNG_FORMAT_GA &&
|
|
tr->st.background_bit_depth == 16U);
|
|
|
|
/* If gamma transforms are eliminated this might fail: */
|
|
debug(tr->st.background_gamma == tc->gamma ||
|
|
tr->st.background_gamma == 0 ||
|
|
tc->sBIT_G == 1);
|
|
|
|
tc->sp = tc->dp; /* nothing else changes */
|
|
|
|
do
|
|
{
|
|
const png_uint_32 alpha = (sp[2] << 8) + sp[3];
|
|
|
|
switch (alpha)
|
|
{
|
|
case 0U: /* transparent */
|
|
memset(dp, 0U, 4U);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
png_uint_32 v = ((sp[0] << 8) + sp[1]) * alpha +
|
|
background * (65535U - alpha);
|
|
|
|
PNG_COMPOSE_DIV_65535(v);
|
|
debug(v <= 65535U);
|
|
dp[0] = PNG_BYTE(v >> 8);
|
|
dp[1] = PNG_BYTE(v);
|
|
}
|
|
|
|
if (compose)
|
|
dp[3] = dp[2] = 0xFFU; /* alpha; set to 1.0 */
|
|
|
|
else if (copy)
|
|
{
|
|
dp[2] = PNG_BYTE(alpha >> 8);
|
|
dp[3] = PNG_BYTE(alpha);
|
|
}
|
|
break;
|
|
|
|
case 65535U: /* opaque */
|
|
if (copy)
|
|
memcpy(dp, sp, 4U);
|
|
break;
|
|
}
|
|
|
|
sp += 4U;
|
|
dp += 4U;
|
|
}
|
|
while (sp < ep);
|
|
|
|
debug(sp == ep+3U);
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_do_background_alpha_RGBA(png_transformp *transform,
|
|
png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc) - 7U/*safety*/;
|
|
const unsigned int bred = tr->st.background.red;
|
|
const unsigned int bgreen = tr->st.background.green;
|
|
const unsigned int bblue = tr->st.background.blue;
|
|
const int copy = (sp != dp);
|
|
const int compose = tr->st.compose_background;
|
|
|
|
affirm(tc->bit_depth == 16U && tc->format == PNG_FORMAT_RGBA &&
|
|
tr->st.background_bit_depth == 16U);
|
|
|
|
debug(tr->st.background_gamma == tc->gamma ||
|
|
tr->st.background_gamma == 0 ||
|
|
(tc->sBIT_R == 1 && tc->sBIT_G == 1 && tc->sBIT_B == 1));
|
|
|
|
tc->sp = tc->dp; /* nothing else changes */
|
|
|
|
do
|
|
{
|
|
const png_uint_32 alpha = (sp[6] << 8) + sp[7];
|
|
|
|
switch (alpha)
|
|
{
|
|
case 0U: /* transparent */
|
|
memset(dp, 0U, 8U);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
const png_uint_32 balpha = (65535U - alpha);
|
|
png_uint_32 r = ((sp[0] << 8) + sp[1]) * alpha + bred * balpha;
|
|
png_uint_32 g = ((sp[2] << 8) + sp[3]) * alpha + bgreen * balpha;
|
|
png_uint_32 b = ((sp[4] << 8) + sp[5]) * alpha + bblue * balpha;
|
|
|
|
PNG_COMPOSE_DIV_65535(r);
|
|
PNG_COMPOSE_DIV_65535(g);
|
|
PNG_COMPOSE_DIV_65535(b);
|
|
debug(r <= 65535U && g <= 65535U && b <= 65535U);
|
|
dp[0] = PNG_BYTE(r >> 8);
|
|
dp[1] = PNG_BYTE(r);
|
|
dp[2] = PNG_BYTE(g >> 8);
|
|
dp[3] = PNG_BYTE(g);
|
|
dp[4] = PNG_BYTE(b >> 8);
|
|
dp[5] = PNG_BYTE(b);
|
|
}
|
|
|
|
if (compose)
|
|
dp[7] = dp[6] = 0xFFU;
|
|
|
|
else if (copy)
|
|
{
|
|
dp[6] = PNG_BYTE(alpha >> 8);
|
|
dp[7] = PNG_BYTE(alpha);
|
|
}
|
|
break;
|
|
|
|
case 65535U: /* opaque */
|
|
if (copy)
|
|
memcpy(dp, sp, 8U);
|
|
break;
|
|
}
|
|
|
|
sp += 8U;
|
|
dp += 8U;
|
|
}
|
|
while (sp < ep);
|
|
|
|
debug(sp == ep+7U);
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_init_background_alpha_end(png_transformp *transform,
|
|
png_transform_controlp tc)
|
|
/* This is just the last part of png_init_background_alpha (below) */
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
|
|
debug(tc->init == PNG_TC_INIT_FINAL);
|
|
|
|
/* Repeat the tests at the end of png_init_background_alpha: */
|
|
affirm(tc->bit_depth == 16U && (tc->format & PNG_FORMAT_FLAG_ALPHA) != 0);
|
|
debug(tc->gamma == 0 ||
|
|
!png_gamma_significant(png_ptr, tc->gamma, tc_sBIT(tc)));
|
|
|
|
/* tr->st.background_is_gray was filled in by resolve_background_color and
|
|
* records if either the background was a gray value or it was a color
|
|
* value with all the channels equal.
|
|
*/
|
|
if (!tr->st.background_is_gray && !(tc->format & PNG_FORMAT_FLAG_COLOR))
|
|
{
|
|
# ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
|
|
/* Color background with gray data: this happens when there is a
|
|
* gray to RGB transform in the pipeline but it hasn't happened
|
|
* yet. Unfortunately it has to happen now to be able to do the
|
|
* compose against the colored background.
|
|
*/
|
|
png_push_gray_to_rgb_byte_ops(transform, tc);
|
|
affirm((tc->format & PNG_FORMAT_FLAG_COLOR) != 0);
|
|
return;
|
|
# else /* !GRAY_TO_RGB */
|
|
impossible("gray to RGB"); /* how can this happen? */
|
|
# endif /* !GRAY_TO_RGB */
|
|
}
|
|
|
|
/* The transform happens in two parts, a part to do the arithmetic on
|
|
* pixels where it is required followed by a part to replace transparent
|
|
* pixels. These two parts require different versions of the background
|
|
* pixel. Set up the second part first.
|
|
*
|
|
* This only happens with background composition, otherwise the
|
|
* transparent pixels are already 0 and nothing needs to be done.
|
|
*/
|
|
if (tr->st.compose_background)
|
|
{
|
|
/* The transparent pixel handling happens *after* the data has been
|
|
* re-encoded to the output gamma:
|
|
*/
|
|
png_transform_background *tr_alpha =
|
|
png_transform_cast(png_transform_background,
|
|
png_add_transform(png_ptr, sizeof (png_transform_background),
|
|
png_init_background_transparent, PNG_TR_GAMMA_ENCODE+0xF0U));
|
|
|
|
/* Copy the current state into the new png_transform_background: */
|
|
tr_alpha->st = tr->st;
|
|
tr_alpha->tr.args = tr->tr.args;
|
|
}
|
|
|
|
/* Now it is possible to overwrite tr->st.background with the linear version.
|
|
*/
|
|
gamma_correct_background(tr, tc);
|
|
|
|
/* sBIT informationmust also be invalidated here, because a gamma
|
|
* transform may run before the transparent pixel handling.
|
|
*/
|
|
tc->invalid_info |= PNG_INFO_sBIT;
|
|
tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
|
|
png_check_byte(png_ptr, tc->bit_depth);
|
|
|
|
/* And select an appropriate function; there are only two choices: */
|
|
switch (tc->format)
|
|
{
|
|
case PNG_FORMAT_GA:
|
|
/* If the background format is color this indicates that there is a
|
|
* gray to RGB transform missing and we need it to happen before
|
|
* this point!
|
|
*/
|
|
affirm(tr->st.background_is_gray);
|
|
tr->tr.fn = png_do_background_alpha_GA;
|
|
break;
|
|
|
|
case PNG_FORMAT_RGBA:
|
|
if (tr->st.background_is_gray)
|
|
tr->st.background.blue = tr->st.background.green =
|
|
tr->st.background.red = tr->st.background.gray;
|
|
tr->tr.fn = png_do_background_alpha_RGBA;
|
|
break;
|
|
|
|
default:
|
|
NOT_REACHED;
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
static void
|
|
png_init_background_alpha(png_transformp *transform, png_transform_controlp tc)
|
|
/* This is used when alpha composition is required because the alpha channel
|
|
* may contain values that are between 0 and 1. Because doing alpha
|
|
* composition requires linear arithmetic the data is converted to 16-bit
|
|
* linear, however this means that the background pixel gets converted too
|
|
* and, for 16-bit output, this tends to smash the value. Consequently the
|
|
* algorithm used here is to skip those pixels and use the 'transparent
|
|
* alpha' routines to replace them after the gamma correction step.
|
|
*/
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
|
|
debug(tc->init == PNG_TC_INIT_FINAL);
|
|
/* png_init_background ensures this is true: */
|
|
debug((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0);
|
|
|
|
/* Always push gamma transforms; don't try to optimize the case when they
|
|
* aren't needed because that would be an attempt to duplicate the tests in
|
|
* png_init_gamma and it might now work reliably.
|
|
*
|
|
* Need to push the to-linear transform *before* this transform and add gamma
|
|
* correction afterward to get back to the screen format. Do the afterward
|
|
* bit first to avoid complexity over *transform:
|
|
*/
|
|
{
|
|
png_transform_gamma *tr_end = add_gamma_transform(png_ptr,
|
|
PNG_TR_GAMMA_ENCODE, tc->gamma, 0U/*bit depth*/, 0/*default*/);
|
|
|
|
/* Encoding the alpha channel happens in the last step, so this needs to
|
|
* be set here. Notice that in C++ terms we are very friendly with
|
|
* png_transform_gamma.
|
|
*/
|
|
tr_end->encode_alpha = tr->st.encode_alpha;
|
|
tr_end->optimize_alpha = tr->st.optimize_alpha;
|
|
}
|
|
|
|
{
|
|
/* Now add tr_gamma before this transform, expect it to go in at
|
|
* *transform or the whole thing won't work:
|
|
*/
|
|
png_transform_gamma *tr_gamma = png_transform_cast(png_transform_gamma,
|
|
png_push_transform(png_ptr, sizeof (png_transform_gamma),
|
|
png_init_gamma, transform, NULL/*don't run init*/));
|
|
|
|
/* This must happen before we run png_gamma_init: */
|
|
tr_gamma->to_gamma = PNG_FP_1;
|
|
tr_gamma->to_bit_depth = 16U;
|
|
|
|
/* Now run the this transform; it was pushed before this one, so it gets
|
|
* to do its init first and this function must return as the caller will
|
|
* immediately call here again.
|
|
*/
|
|
debug(*transform == &tr_gamma->tr);
|
|
png_init_gamma(transform, tc);
|
|
affirm(tc->bit_depth == 16U &&
|
|
(tc->format & PNG_FORMAT_FLAG_ALPHA) != 0);
|
|
/* This is only a 'debug' because it needs to replicate the test in
|
|
* png_init_gamma and that is easy to get wrong (a harmless mistake).
|
|
*/
|
|
debug(tc->gamma == 0 ||
|
|
!png_gamma_significant(png_ptr, tc->gamma, tc_sBIT(tc)));
|
|
}
|
|
|
|
/* A transform was pushed, so this transform init will be run again: */
|
|
tr->tr.fn = png_init_background_alpha_end;
|
|
# undef png_ptr
|
|
}
|
|
|
|
/* Handle alpha and tRNS via a background color */
|
|
static void
|
|
png_init_background(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
/* This init function is called right at the start, this means it can get at
|
|
* the tRNS values if appropriate. If not the RGB to gray transform comes
|
|
* next followed by PNG_TR_COMPOSE_ALPHA, which actually does the non-tRNS
|
|
* work.
|
|
*/
|
|
png_structp png_ptr = tc->png_ptr;
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background, *transform);
|
|
|
|
if (tc->init == PNG_TC_INIT_FORMAT)
|
|
{
|
|
/* Background composition removes the alpha channel, so the other
|
|
* operations become irrelevant:
|
|
*/
|
|
if (tr->st.compose_background)
|
|
tr->st.associate_alpha = tr->st.encode_alpha = tr->st.optimize_alpha =
|
|
0U;
|
|
|
|
else if (!tr->st.associate_alpha)
|
|
{
|
|
/* There is nothing to do, delete the whole transform. */
|
|
tr->tr.fn = NULL;
|
|
return;
|
|
}
|
|
|
|
/* Else alpha association ('pre-multiplication') which is achieved by
|
|
* composing on a 0 background. The background color will be black (all
|
|
* zeros) and the background gamma will be zero.
|
|
*/
|
|
|
|
/* Because we are in PNG_TC_INIT_FORMAT no other transforms will have been
|
|
* inserted between this one and an rgb-to-gray transform, so we can find
|
|
* out if rgb-to-gray has been requested:
|
|
*/
|
|
tr->st.rgb_to_gray = tr->tr.next != NULL &&
|
|
tr->tr.next->order == PNG_TR_RGB_TO_GRAY;
|
|
|
|
if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0)
|
|
{
|
|
/* Associated alpha does not strip the alpha channel! */
|
|
if (tr->st.compose_background)
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
|
|
}
|
|
|
|
else if (!tc->palette &&
|
|
png_ptr->num_trans == 1 && !(tc->invalid_info & PNG_INFO_tRNS))
|
|
{
|
|
/* tRNS will be expanded, or handled */
|
|
tc->invalid_info |= PNG_INFO_tRNS;
|
|
if (!tr->st.compose_background)
|
|
{
|
|
tc->format |= PNG_FORMAT_FLAG_ALPHA;
|
|
/* And in this case, only, because we are adding an alpha channel we
|
|
* need to have a channel depth of at least 8:
|
|
*/
|
|
if (tc->bit_depth < 8U)
|
|
tc->bit_depth = 8U;
|
|
}
|
|
}
|
|
|
|
else /* no transparent pixels to change */
|
|
tr->tr.fn = NULL;
|
|
}
|
|
|
|
else /* PNG_TC_INIT_FINAL */
|
|
{
|
|
png_fixed_point correction;
|
|
|
|
debug(tc->init == PNG_TC_INIT_FINAL &&
|
|
((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ||
|
|
(!tc->palette && png_ptr->num_trans == 1 &&
|
|
!(tc->invalid_info & PNG_INFO_tRNS))));
|
|
|
|
/* The screen gamma is known, so the background gamma can be found, note
|
|
* that both the gamma values used below will be 0 if no gamma information
|
|
* was in the PNG and no gamma information has been provided by
|
|
* png_set_gamma or png_set_alpha_mode.
|
|
*/
|
|
switch (tr->st.background_gamma)
|
|
{
|
|
case PNG_BACKGROUND_GAMMA_FILE:
|
|
/* png_init_transform_control has already found the file gamma,
|
|
* and because this is the first arithmetic transformation
|
|
* nothing has changed it.
|
|
*/
|
|
tr->st.background_gamma = tc->gamma;
|
|
break;
|
|
|
|
case PNG_BACKGROUND_GAMMA_SCREEN:
|
|
tr->st.background_gamma = png_ptr->row_gamma;
|
|
break;
|
|
|
|
default:
|
|
/* already set */
|
|
break;
|
|
}
|
|
|
|
/* Work out what the background color is, this only depends on 'tc' for
|
|
* palette information, so it can be done now before we know the actual
|
|
* bit_depth/format that will be required:
|
|
*/
|
|
resolve_background_color(tr, tc);
|
|
|
|
/* Is this format compatible with the current row data? If it is then it
|
|
* is possible to avoid the arithmetic if no alpha processing is required.
|
|
* This is a useful optimization because PNG files with just transparent
|
|
* pixels and no alpha are common.
|
|
*
|
|
* NOTE: if an RGB-to-gray transform is present this is fine so long as
|
|
* the background is gray, otherwise (non-gray background) there is a
|
|
* following gray-to-RGB transform and the now gray image must be
|
|
* composited on a color background.
|
|
*/
|
|
if (tr->st.compose_background /* alpha channel stripped */ &&
|
|
(tr->st.background_is_gray ||
|
|
((tc->format & PNG_FORMAT_FLAG_COLOR) != 0 && !tr->st.rgb_to_gray))
|
|
/* color compatible */ &&
|
|
tc->bit_depth >= tr->st.background_bit_depth
|
|
/* bit depth compatible */ &&
|
|
(tc->transparent_alpha ||
|
|
(!tc->palette && png_ptr->num_trans == 1 &&
|
|
!(tc->invalid_info & PNG_INFO_tRNS)))
|
|
/* no alpha processing */ &&
|
|
png_gamma_equal(png_ptr, tc->gamma, png_ptr->row_gamma, &correction,
|
|
tc->bit_depth) /* gamma compatible (so no gamma processing) */)
|
|
{
|
|
/* How the operation gets performed depends on whether the current data
|
|
* has an alpha channel or not.
|
|
*/
|
|
if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0)
|
|
{
|
|
affirm(tc->transparent_alpha);
|
|
/* This init routine does the sBIT handling: */
|
|
png_init_background_transparent(transform, tc);
|
|
}
|
|
|
|
else if (!tc->palette && png_ptr->num_trans == 1 &&
|
|
!(tc->invalid_info & PNG_INFO_tRNS))
|
|
{
|
|
/* The background pixel needs to be filled in now; no more init
|
|
* routines are called in this case. It is important to delay this
|
|
* as late as possible because it needs to know the actual tc format
|
|
* that must be used.
|
|
*/
|
|
fill_background_pixel(tr, tc);
|
|
|
|
debug(!(png_ptr->color_type & PNG_COLOR_MASK_PALETTE));
|
|
|
|
/* The pixel depth should not have been changed yet: */
|
|
debug(PNG_PIXEL_DEPTH(*png_ptr) == PNG_TC_PIXEL_DEPTH(*tc));
|
|
|
|
/* The transparent_pixel value needs to be filled in. */
|
|
affirm(tr->st.ntrans ==
|
|
fill_transparent_pixel(png_ptr, tr->st.transparent_pixel));
|
|
|
|
/* The whole operation is a no-op if the transparent pixel and the
|
|
* background pixel match, even in the associated alpha case where
|
|
* both will be 0 throughout.
|
|
*
|
|
* NOTE: for palette images this test happens in the caching
|
|
* operation, so the answer is still correct.
|
|
*
|
|
* NOTE: for low bit depth gray both 'transparent_pixel' and
|
|
* 'background_pixel' have been expanded to fill a byte, so this
|
|
* works.
|
|
*/
|
|
if (memcmp(tr->st.transparent_pixel, tr->st.background_pixel,
|
|
tr->st.ntrans) == 0)
|
|
tr->tr.fn = NULL;
|
|
|
|
/* Then the processing function depends on the pixel size: */
|
|
else if (tr->st.ntrans > 1U)
|
|
tr->tr.fn = png_do_replace_tRNS_multi;
|
|
|
|
else if (tc->bit_depth == 8U)
|
|
tr->tr.fn = png_do_replace_tRNS_8;
|
|
|
|
else if (tc->bit_depth == 1U)
|
|
{
|
|
/* This is the silly case: the replacement pixel does not match
|
|
* the transparent pixel (handled above) so either all the '0'
|
|
* bits are replaced by '1' or all the '1' bits are replaced by
|
|
* '0':
|
|
*/
|
|
png_uint_32 args = tr->st.background_pixel[0];
|
|
|
|
args <<= 24;
|
|
args |= PNG_INFO_tRNS | PNG_INFO_sRGB;
|
|
tr->tr.args = args;
|
|
tr->tr.fn = png_do_set_row;
|
|
}
|
|
|
|
else
|
|
tr->tr.fn = png_do_replace_tRNS_lbd;
|
|
|
|
tc->invalid_info |= PNG_INFO_tRNS | PNG_INFO_sBIT;
|
|
tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A =
|
|
png_check_byte(png_ptr, tc->bit_depth);
|
|
}
|
|
|
|
else
|
|
{
|
|
/* Nothing to do; should have been eliminated before! */
|
|
tr->tr.fn = NULL;
|
|
NOT_REACHED;
|
|
}
|
|
}
|
|
|
|
else /* alpha, or maybe gamma, processing required */
|
|
{
|
|
/* Alpha case, add an appropriate transform; this has to be done
|
|
* *after* the RGB-to-gray case so move the transform info there:
|
|
*/
|
|
png_transform_background *tr_alpha =
|
|
png_transform_cast(png_transform_background,
|
|
png_add_transform(png_ptr, sizeof (png_transform_background),
|
|
png_init_background_alpha, PNG_TR_COMPOSE_ALPHA));
|
|
|
|
/* Copy the current state into the new png_transform_background: */
|
|
tr_alpha->st = tr->st;
|
|
tr_alpha->tr.args = tr->tr.args;
|
|
|
|
/* The rest of the init occurs later; this transform is no longer
|
|
* needed.
|
|
*/
|
|
tr->tr.fn = NULL;
|
|
|
|
/* Ensure that png_init_background_alpha gets an alpha channel, this
|
|
* needs to happen here because otherwise intervening transforms can
|
|
* invalidate tRNS.
|
|
*/
|
|
tc->expand_tRNS = 1U;
|
|
if (tr->st.compose_background)
|
|
tc->strip_alpha = 0U;
|
|
|
|
/* And push the expand: */
|
|
(void)push_gamma_expand(transform, tc, 1/*need alpha*/);
|
|
|
|
/* Regardless of whether anything got pushed the following should now
|
|
* be true:
|
|
*/
|
|
affirm((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 &&
|
|
tc->bit_depth >= 8U);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PNGFAPI
|
|
png_set_background_fixed(png_structrp png_ptr,
|
|
png_const_color_16p background_color, int background_gamma_code,
|
|
int need_expand, png_fixed_point background_gamma)
|
|
{
|
|
if (png_ptr != NULL)
|
|
{
|
|
if (background_color != NULL)
|
|
{
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background,
|
|
png_add_transform(png_ptr, sizeof (png_transform_background),
|
|
png_init_background, PNG_TR_COMPOSE));
|
|
|
|
/* This silently overwrites the information if png_set_background is
|
|
* called more than once.
|
|
*/
|
|
tr->st.background = *background_color;
|
|
tr->st.need_expand = need_expand != 0;
|
|
tr->st.compose_background = 1U; /* png_set_background called */
|
|
switch (background_gamma_code)
|
|
{
|
|
case PNG_BACKGROUND_GAMMA_SCREEN:
|
|
case PNG_BACKGROUND_GAMMA_FILE:
|
|
tr->st.background_gamma = background_gamma_code;
|
|
break;
|
|
|
|
case PNG_BACKGROUND_GAMMA_UNIQUE:
|
|
if (background_gamma >= 16 && background_gamma <= 625000000)
|
|
{
|
|
tr->st.background_gamma = background_gamma;
|
|
break;
|
|
}
|
|
|
|
png_app_error(png_ptr, "gamma value out of range");
|
|
/* FALL THROUGH */
|
|
default:
|
|
png_app_error(png_ptr, "invalid gamma information");
|
|
tr->st.background_gamma = (need_expand ?
|
|
PNG_BACKGROUND_GAMMA_FILE : PNG_BACKGROUND_GAMMA_SCREEN);
|
|
break;
|
|
}
|
|
}
|
|
|
|
else
|
|
png_app_error(png_ptr, "missing background color");
|
|
}
|
|
}
|
|
|
|
# ifdef PNG_FLOATING_POINT_SUPPORTED
|
|
void PNGAPI
|
|
png_set_background(png_structrp png_ptr,
|
|
png_const_color_16p background_color, int background_gamma_code,
|
|
int need_expand, double background_gamma)
|
|
{
|
|
png_set_background_fixed(png_ptr, background_color, background_gamma_code,
|
|
need_expand, png_fixed(png_ptr, background_gamma, "png_set_background"));
|
|
}
|
|
# endif /* FLOATING_POINT */
|
|
#endif /* READ_BACKGROUND */
|
|
|
|
#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
|
|
void PNGFAPI
|
|
png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,
|
|
png_fixed_point output_gamma)
|
|
{
|
|
if (png_ptr != NULL)
|
|
{
|
|
/* Check the passed in output_gamma value; it must be valid and it must be
|
|
* converted to the reciprocal for use below:
|
|
*/
|
|
output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/);
|
|
|
|
if (output_gamma > 0) /* Else an app_error has been signalled. */
|
|
{
|
|
/* Only set the colorspace gamma if it has not already been set (this
|
|
* has the side effect that the gamma in a second call to
|
|
* png_set_alpha_mode will be ignored.)
|
|
*/
|
|
if ((png_ptr->colorspace.flags &
|
|
(PNG_COLORSPACE_INVALID | PNG_COLORSPACE_HAVE_GAMMA)) !=
|
|
PNG_COLORSPACE_HAVE_GAMMA)
|
|
{
|
|
/* The default file gamma is the output gamma encoding: */
|
|
png_ptr->colorspace.gamma = output_gamma;
|
|
if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)
|
|
png_ptr->colorspace.flags = PNG_COLORSPACE_HAVE_GAMMA;
|
|
else
|
|
png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
|
|
}
|
|
|
|
/* Always set the output gamma, note that it may be changed to PNG_FP_1
|
|
* for the associated alpha support. This means that the last call to
|
|
* png_set_gamma[_fixed] or png_set_alpha_mode sets the output gamma,
|
|
* which is probably what is expected.
|
|
*/
|
|
{
|
|
png_transform_gamma *tr_gamma = add_gamma_transform(png_ptr,
|
|
PNG_TR_GAMMA_ENCODE,
|
|
mode == PNG_ALPHA_ASSOCIATED ? PNG_FP_1 : output_gamma, 0U,
|
|
1/*force*/);
|
|
|
|
/* Get a background transform and set the appropriate fields.
|
|
*
|
|
* png_set_background removes the alpha channel so it effectively
|
|
* disbles png_set_alpha_mode however png_set_alpha_mode is still
|
|
* useful to set a default gamma value.
|
|
*/
|
|
png_transform_background *tr =
|
|
png_transform_cast(png_transform_background,
|
|
png_add_transform(png_ptr, sizeof (png_transform_background),
|
|
png_init_background, PNG_TR_COMPOSE));
|
|
|
|
/* There are really 8 possibilities here, composed of any
|
|
* combination of:
|
|
*
|
|
* premultiply the color channels
|
|
* do not encode non-opaque pixels (leave as linear)
|
|
* encode the alpha as well as the color channels
|
|
*
|
|
* The differences disappear if the input/output ('screen') gamma is
|
|
* 1.0, because then the encoding is a no-op and there is only the
|
|
* choice of premultiplying the color channels or not.
|
|
*/
|
|
switch (mode)
|
|
{
|
|
case PNG_ALPHA_PNG: /* default: png standard */
|
|
/* No compose, but it may be set by png_set_background! This
|
|
* is the only mode that doesn't interfere with what
|
|
* png_set_background does.
|
|
*/
|
|
tr->st.associate_alpha = 0U;
|
|
tr_gamma->encode_alpha = tr->st.encode_alpha = 0U;
|
|
tr_gamma->optimize_alpha = tr->st.optimize_alpha = 0U;
|
|
break;
|
|
|
|
case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */
|
|
tr->st.associate_alpha = 1U;
|
|
tr_gamma->encode_alpha = tr->st.encode_alpha = 0U;
|
|
tr_gamma->optimize_alpha = tr->st.optimize_alpha = 0U;
|
|
break;
|
|
|
|
case PNG_ALPHA_OPTIMIZED:
|
|
/* associated with opaque pixels having the given gamma and
|
|
* non-opaque pixels being linear.
|
|
*/
|
|
tr->st.associate_alpha = 1U;
|
|
tr_gamma->encode_alpha = tr->st.encode_alpha = 0U;
|
|
tr_gamma->optimize_alpha = tr->st.optimize_alpha = 1U;
|
|
/* output_gamma records the encoding of opaque pixels! */
|
|
break;
|
|
|
|
case PNG_ALPHA_BROKEN:
|
|
/* associated+non-linear+alpha encoded */
|
|
tr->st.associate_alpha = 1U;
|
|
tr_gamma->encode_alpha = tr->st.encode_alpha = 1U;
|
|
tr_gamma->optimize_alpha = tr->st.optimize_alpha = 0U;
|
|
break;
|
|
|
|
default:
|
|
png_app_error(png_ptr, "invalid alpha mode");
|
|
/* A return at this point is safe; if a background transform
|
|
* was created the init routine will remove it because
|
|
* nothing is set.
|
|
*/
|
|
break;
|
|
} /* alpha mode switch */
|
|
} /* add gamma and background transforms */
|
|
} /* valid output gamma */
|
|
} /* png_ptr != NULL */
|
|
}
|
|
|
|
#ifdef PNG_FLOATING_POINT_SUPPORTED
|
|
void PNGAPI
|
|
png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma)
|
|
{
|
|
png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr,
|
|
output_gamma));
|
|
}
|
|
#endif /* FLOATING_POINT */
|
|
#endif /* READ_ALPHA_MODE */
|
|
|
|
#ifdef PNG_READ_TRANSFORMS_SUPPORTED
|
|
typedef struct
|
|
{
|
|
png_transform tr;
|
|
png_transform_control tc;
|
|
union
|
|
{
|
|
png_uint_32 u32[1]; /* ensure alignment */
|
|
png_uint_16 u16[1];
|
|
png_byte b8[1];
|
|
} cache;
|
|
} png_transform_cache;
|
|
|
|
#define png_transform_cache_size(size)\
|
|
(offsetof(png_transform_cache, cache)+(size))
|
|
#define png_transform_cache_cast(pointer,size)\
|
|
png_voidcast(png_transform_cache*,\
|
|
png_transform_cast_check(png_ptr, PNG_SRC_LINE, (pointer),\
|
|
png_transform_cache_size(size)))
|
|
/* This is like png_transform_cast except that 'size' is the size of the
|
|
* cache part in the above structure and the type returned is always
|
|
* 'png_transform_cache*'.
|
|
*/
|
|
|
|
/* Functions to handle the cache operation. These don't do any initialization;
|
|
* that happens below when PNG_TC_INIT_FINAL is being run on the whole list.
|
|
* These functions are only implemented for read so the transform control
|
|
* source and destination are always aligned.
|
|
*
|
|
* First some utility functions:
|
|
*/
|
|
static void
|
|
png_transform_control_cp(png_transform_controlp tcDest,
|
|
png_const_transform_controlp tcSrc)
|
|
{
|
|
/* Copy tcSrc over tcDest without overwriting the information specific to the
|
|
* row being transformed.
|
|
*/
|
|
png_structp png_ptr = tcDest->png_ptr;
|
|
png_const_voidp sp = tcDest->sp;
|
|
png_voidp dp = tcDest->dp;
|
|
png_uint_32 width = tcDest->width;
|
|
unsigned int init = tcDest->init;
|
|
|
|
*tcDest = *tcSrc;
|
|
|
|
tcDest->png_ptr = png_ptr;
|
|
tcDest->sp = sp;
|
|
tcDest->dp = dp;
|
|
tcDest->width = width;
|
|
tcDest->init = png_check_bits(tcDest->png_ptr, init, 2);
|
|
}
|
|
|
|
#if !PNG_RELEASE_BUILD
|
|
static int
|
|
png_transform_control_eq(png_const_transform_controlp tc1,
|
|
png_const_transform_controlp tc2)
|
|
{
|
|
/* Say if *tc1 == *tc2, ignoring differences in uncopied fields and 'cost':
|
|
*/
|
|
return
|
|
# ifdef PNG_READ_GAMMA_SUPPORTED
|
|
tc1->gamma == tc2->gamma &&
|
|
# endif
|
|
tc1->format == tc2->format &&
|
|
tc1->range == tc2->range &&
|
|
tc1->bit_depth == tc2->bit_depth &&
|
|
tc1->caching == tc2->caching &&
|
|
tc1->palette == tc2->palette;
|
|
/* invalid_info, cost, interchannel and channel_add are only set during
|
|
* init, so don't do the compare.
|
|
*/
|
|
}
|
|
#endif /* !RELEASE_BUILD */
|
|
|
|
/* Now the routines that actually perform the transform. There are two basic
|
|
* cases:
|
|
*
|
|
* 1) A cached transform that does not change the pixel size and where the pixel
|
|
* size 8 bits or less. This can be done by a 256-entry single byte lookup
|
|
* table, regardless of the bit depth. Two versions of the code exist, one
|
|
* which just transforms the row, the other which transforms and records the
|
|
* maximum pixel depth.
|
|
*
|
|
* 2) A cached transform that increases pixel depth. The destination pixel
|
|
* depth will always be a multiple of 8 bits, the source pixel will be less
|
|
* than or equal to 8 bits and will be in the PNG native (big endian) layout.
|
|
*/
|
|
#define png_ptr (tc->png_ptr) /* Used in all functions below */
|
|
/* (1): single-byte cached transforms: */
|
|
static void
|
|
do_transform_cache_byte(png_transformp *trIn, png_transform_controlp tc)
|
|
{
|
|
png_transform_cache *tr = png_transform_cache_cast(*trIn, 256U);
|
|
|
|
/* Copy the bytes through the 256-byte LUT: */
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep ep = dp + PNG_TC_ROWBYTES(*tc);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
|
|
tc->sp = dp;
|
|
|
|
do
|
|
*dp++ = tr->cache.b8[*sp++];
|
|
while (dp < ep);
|
|
|
|
png_transform_control_cp(tc, &tr->tc);
|
|
}
|
|
|
|
/* (2) A cached transform that increases pixel depth.
|
|
*
|
|
* There are six output depth possibilites, all a whole number of bytes:
|
|
*
|
|
* 1 byte, 8 bits: palette or grayscale
|
|
* 2 bytes, 16 bits: 16-bit grayscale or 8-bit gray+alpa
|
|
* 3 bytes, 24 bits: 8-bit RGB
|
|
* 4 bytes, 32 bits: 16-bit gray+alpha or 8-bit RGBA
|
|
* 6 bytes, 48 bits: 16-bit RGB
|
|
* 8 bytes, 64 bits: 16-bit RGBA
|
|
*
|
|
* The input must be 1, 2, 4 or 8-bit gray or palette. The first 1-byte case is
|
|
* handled for 8-bit gray/palette above, so there are 22 possibilities. The
|
|
* function names below are:
|
|
*
|
|
* do_transform_cache_<input-bits>_<output-bits>
|
|
*/
|
|
#define transform_cache_size(ipd,opd) ((((1U << (ipd)) * (opd))+7U) >> 3)
|
|
static void
|
|
do_transform_cache_(png_transformp *trIn, png_transform_controlp tc,
|
|
unsigned int ipd, unsigned int opd)
|
|
/* This is the implementation for unknown ipd, opd, below it is called with
|
|
* fixed values. The purpose of this is to allow the compiler/system builder
|
|
* to decide how to optimize for size vs space vs speed. Note that this
|
|
* implementation, while it would work for 8 bit ipd, is not used in that
|
|
* case.
|
|
*/
|
|
{
|
|
png_transform_cache *tr =
|
|
png_transform_cache_cast(*trIn, transform_cache_size(ipd, opd));
|
|
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep ep = dp;
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
unsigned int s, shift, mask;
|
|
|
|
sp += PNG_TC_ROWBYTES(*tc); /* One byte beyond the end */
|
|
|
|
png_transform_control_cp(tc, &tr->tc);
|
|
dp += PNG_TC_ROWBYTES(*tc);
|
|
|
|
shift = 7U & -(tc->width * ipd);
|
|
/* MSB: shift right required to get last pixel */
|
|
mask = (1U << ipd) - 1U;
|
|
/* Mask to extract a single pixel from the low bits of a byte */
|
|
opd >>= 3;
|
|
/* Output pixel size in bytes */
|
|
s = *--sp;
|
|
/* The first byte; the last byte of the input row */
|
|
|
|
for (;;)
|
|
{
|
|
png_const_bytep opixel = (((s >> shift) & mask)+1U) * opd + tr->cache.b8;
|
|
/* Points to the byte after last byte of the output value */
|
|
unsigned int i;
|
|
|
|
for (i=0; i<opd; ++i)
|
|
*--dp = *--opixel;
|
|
|
|
if (dp <= ep)
|
|
break;
|
|
|
|
shift += ipd; /* To find shift for *previous* pixel */
|
|
|
|
if (shift == 8U)
|
|
s = *--sp, shift = 0U/*right-most pixel*/;
|
|
}
|
|
|
|
debug(dp == ep && shift == 8U-ipd && sp == tc->sp);
|
|
tc->sp = ep; /* start of row, safe even if the above fails */
|
|
}
|
|
|
|
#define do_transform_cache(ipd,opd)\
|
|
static void \
|
|
do_transform_cache_##ipd##_##opd(png_transformp *tr, png_transform_controlp tc)\
|
|
{\
|
|
do_transform_cache_(tr, tc, ipd, opd);\
|
|
}
|
|
|
|
#define TCLOW(opd)\
|
|
do_transform_cache(1,opd)\
|
|
do_transform_cache(2,opd)\
|
|
do_transform_cache(4,opd)
|
|
|
|
TCLOW(8)
|
|
TCLOW(16)
|
|
TCLOW(24)
|
|
TCLOW(32)
|
|
TCLOW(48)
|
|
TCLOW(64)
|
|
|
|
#undef TCLOW
|
|
#undef do_transform_cache
|
|
|
|
static void
|
|
do_transform_cache_8_(png_transformp *trIn, png_transform_controlp tc,
|
|
unsigned int opd)
|
|
/* This is the 8-bit input implementation. */
|
|
{
|
|
png_transform_cache *tr =
|
|
png_transform_cache_cast(*trIn, transform_cache_size(8, opd));
|
|
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
png_const_bytep ep = dp;
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
|
|
sp += PNG_TC_ROWBYTES(*tc); /* One byte beyond the end */
|
|
|
|
png_transform_control_cp(tc, &tr->tc);
|
|
dp += PNG_TC_ROWBYTES(*tc);
|
|
|
|
opd >>= 3; /* Output pixel size in bytes */
|
|
do
|
|
{
|
|
png_const_bytep opixel = (*--sp + 1U) * opd + tr->cache.b8;
|
|
/* Points to the byte after last byte of the output value */
|
|
unsigned int i;
|
|
|
|
for (i=0; i<opd; ++i)
|
|
*--dp = *--opixel;
|
|
}
|
|
while (dp > ep);
|
|
|
|
debug(dp == ep && sp == tc->sp);
|
|
tc->sp = ep; /* start of row, safe even if the above fails */
|
|
}
|
|
|
|
#define do_transform_cache(opd)\
|
|
static void \
|
|
do_transform_cache_8_##opd(png_transformp *tr, png_transform_controlp tc)\
|
|
{\
|
|
do_transform_cache_8_(tr, tc, opd);\
|
|
}
|
|
|
|
/* The 8-bit to 8-bit case uses the byte transform code */
|
|
do_transform_cache(16)
|
|
do_transform_cache(24)
|
|
do_transform_cache(32)
|
|
do_transform_cache(48)
|
|
do_transform_cache(64)
|
|
|
|
#undef do_transform_cache
|
|
|
|
#define do_transform_cache(ipd,opd) do_transform_cache_##ipd##_##opd
|
|
|
|
#undef png_ptr
|
|
|
|
typedef struct
|
|
{
|
|
png_transformp *start;
|
|
/* This is a pointer to the pointer to the start of the list being cached,
|
|
* i.e. *start is the first transform in the list.
|
|
*/
|
|
png_transform_control tstart;
|
|
/* This is the transform control at the start; i.e. before (*start)->fn is
|
|
* called. Note that for palette data it will contain the original
|
|
* palette format/bit-depth, not that passed to (*start)->fn which will
|
|
* represent the palette.
|
|
*/
|
|
png_transformp *end;
|
|
png_transform_control tend;
|
|
/* The same data from the end of the run to be cached, i.e. after the
|
|
* function of the transform which *contains* '*end' (end points to
|
|
* tr->next).
|
|
*/
|
|
} png_cache_params, *png_cache_paramsp;
|
|
|
|
static void
|
|
init_caching(png_structp png_ptr, png_cache_paramsp cp)
|
|
/* Given an already initialized cp->tend turn on caching if appropriate. */
|
|
{
|
|
/* Handle the colormap case, where a cache is always required: */
|
|
if (cp->tend.format & PNG_FORMAT_FLAG_COLORMAP)
|
|
{
|
|
/* This turns starts the palette caching with the next transform: */
|
|
cp->tend.palette = cp->tend.caching = 1U;
|
|
cp->tend.transparent_alpha = png_ptr->transparent_palette;
|
|
cp->tend.format = PNG_FORMAT_FLAG_COLOR;
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
if (png_ptr->num_trans > 0 && !(cp->tend.invalid_info & PNG_INFO_tRNS))
|
|
{
|
|
cp->tend.format |= PNG_FORMAT_FLAG_ALPHA;
|
|
}
|
|
# endif /* READ_tRNS */
|
|
cp->tend.bit_depth = 8U;
|
|
}
|
|
|
|
else if (PNG_TC_PIXEL_DEPTH(cp->tend) <= 8)
|
|
{
|
|
/* Cacheable pixel transforms; the pixel is less than 8 bits in size so
|
|
* the cache makes sense.
|
|
*
|
|
* TODO: check the cost estimate and the image size to avoid expensive
|
|
* caches of very small images.
|
|
*/
|
|
cp->tend.caching = 1U;
|
|
}
|
|
|
|
/* TODO: handle handle 8-bit GA/RGB/RGBA */
|
|
}
|
|
|
|
static void
|
|
add_cache_transform(png_structp png_ptr, unsigned int order,
|
|
png_transform_fn fn, png_cache_paramsp cp,
|
|
png_const_bytep cache, unsigned int size)
|
|
/* Add a transform from the input format cp->tstart to the output format
|
|
* stored in cp->tend.
|
|
*/
|
|
{
|
|
affirm(size <= 2048U); /* 256 8-byte pixels at most */
|
|
{
|
|
png_transform_cache *tr = png_transform_cache_cast(
|
|
png_add_transform(png_ptr, png_transform_cache_size(size), fn, order),
|
|
size);
|
|
|
|
/* This must have replaced the transform in *cp->start: */
|
|
affirm(&tr->tr == *cp->start);
|
|
|
|
/* Fill in the respective members: */
|
|
tr->tc = cp->tend;
|
|
memcpy(tr->cache.b8, cache, size);
|
|
|
|
/* Skip this transform, because the calling routine has already executed
|
|
* the cache (it could be executed again, just to verify that it works;
|
|
* cp->tstart should be correct.)
|
|
*/
|
|
cp->start = &tr->tr.next;
|
|
}
|
|
}
|
|
|
|
static unsigned int
|
|
setup_palette_cache(png_structp png_ptr, png_byte cache[8*256])
|
|
/* This returns the number of entries in the cache; the width */
|
|
{
|
|
const unsigned int num_palette = png_ptr->num_palette;
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
unsigned int num_trans = png_ptr->num_trans;
|
|
# endif /* READ_tRNS */
|
|
const png_colorp palette = png_ptr->palette;
|
|
png_bytep p;
|
|
unsigned int i;
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
const png_bytep trans_alpha = png_ptr->trans_alpha;
|
|
# endif /* READ_tRNS */
|
|
|
|
for (i=0, p=cache; i<num_palette; ++i)
|
|
{
|
|
*p++ = palette[i].red;
|
|
*p++ = palette[i].green;
|
|
*p++ = palette[i].blue;
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
if (num_trans > 0)
|
|
{
|
|
if (i < num_trans)
|
|
*p++ = trans_alpha[i];
|
|
|
|
else
|
|
*p++ = 0xFFU;
|
|
}
|
|
# endif /* READ_tRNS */
|
|
}
|
|
|
|
return num_palette;
|
|
}
|
|
|
|
static void
|
|
png_remove_PLTE_and_tRNS(png_structrp png_ptr)
|
|
{
|
|
if (png_ptr->palette != NULL)
|
|
png_free(png_ptr, png_ptr->palette);
|
|
|
|
png_ptr->palette = NULL;
|
|
png_ptr->num_palette = 0;
|
|
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
if (png_ptr->trans_alpha != NULL)
|
|
png_free(png_ptr, png_ptr->trans_alpha);
|
|
|
|
png_ptr->trans_alpha = NULL;
|
|
png_ptr->num_trans = 0;
|
|
# endif /* READ_tRNS */
|
|
|
|
png_ptr->palette_updated = 1U;
|
|
}
|
|
|
|
static void
|
|
update_palette(png_structp png_ptr, png_cache_paramsp cp,
|
|
unsigned int max_depth)
|
|
{
|
|
union
|
|
{
|
|
png_uint_32 u32[1];
|
|
png_uint_16 u16[1]; /* For alignment */
|
|
png_byte b8[8*256]; /* For 16-bit RGBA intermediate */
|
|
} cache;
|
|
|
|
/* The caller only calls this function if the initial transform control had
|
|
* the palette flag set, implying that the original 'format' was a COLORMAP
|
|
* one. Also this can only happen (at present) when starting the transform
|
|
* list, so:
|
|
*/
|
|
affirm((cp->tstart.format & PNG_FORMAT_FLAG_COLORMAP) != 0); /* required */
|
|
debug(cp->start == &png_ptr->transform_list); /* should be harmless */
|
|
|
|
/* Run the whole of the given list on the palette data. PNG_TC_INIT_FINAL
|
|
* has already been run; this is a full run (with init == 0).
|
|
*/
|
|
{
|
|
unsigned int check_depth;
|
|
only_deb(png_transform_control orig = cp->tend;)
|
|
|
|
cp->tend = cp->tstart;
|
|
init_caching(png_ptr, cp);
|
|
/* And set up tend to actually work out the palette: */
|
|
cp->tend.init = 0U;
|
|
cp->tend.width = setup_palette_cache(png_ptr, cache.b8);
|
|
cp->tend.sp = cache.b8;
|
|
cp->tend.dp = cache.b8;
|
|
|
|
check_depth =
|
|
png_run_this_transform_list_forwards(&cp->tend, cp->start, *cp->end);
|
|
|
|
/* If we get here these two things must be true or there are been some
|
|
* buggy difference of opinion between the INIT code and the actual run:
|
|
*/
|
|
affirm(check_depth == max_depth && cp->tend.palette);
|
|
|
|
/* This should match the passed in final format obtained before, this
|
|
* debug statement detects discrepancies between the init code and the
|
|
* run code:
|
|
*/
|
|
debug(png_transform_control_eq(&cp->tend, &orig));
|
|
|
|
/* Also, expect the palette to still be valid: */
|
|
debug((cp->tend.invalid_info & PNG_INFO_PLTE) == 0);
|
|
}
|
|
|
|
/* The result must be compatible with a PNG palette with respect to bit
|
|
* depth; specifically the expand-16 transform has no effect on palette data.
|
|
*
|
|
* The colormap setting must not have been re-introduced here either; there
|
|
* may be some quantize interactions here, neither can unexpected flags be
|
|
* handled; just COLOR and ALPHA.
|
|
*/
|
|
affirm(cp->tend.bit_depth == 8 &&
|
|
(cp->tend.format & PNG_FORMAT_FLAG_COLORMAP) == 0);
|
|
|
|
/* Remove all the transforms between start(inclusive) and end(exclusive);
|
|
* they have been processed. The effect they had on the transform control
|
|
* is irrelevant because the caller re-instates the settings from tstart.
|
|
*/
|
|
{
|
|
png_transformp list = *cp->start; /* list to free */
|
|
|
|
*cp->start = *cp->end; /* part of list not to be freed */
|
|
*cp->end = NULL; /* terminate the list to be freed */
|
|
cp->end = cp->start; /* else cp->end points to the end of the list! */
|
|
|
|
png_transform_free(png_ptr, &list);
|
|
}
|
|
|
|
/* Adjust the PNG palette and, if required, the tRNS entries. Note that
|
|
* if the transforms stripped the alpha channel from the palette num_trans
|
|
* will get set to 0 here.
|
|
*
|
|
* This is the point where the gamma gets frozen too. The alternative
|
|
* design is to pass palette, tRNS and gamma up the transform chain, but
|
|
* that doesn't work because the palette change would, apparently, have to
|
|
* be repeated on each row. This seems simpler at the cost of a little
|
|
* obscurity; the answer to the question, "Where does the palette get
|
|
* updated?", is "Here!"
|
|
*
|
|
* API CHANGE: (fix): previously the init code would silently overwrite
|
|
* the palette information shared with png_info, breaking the API for
|
|
* png_read_update_info, which doesn't update the info if it isn't called,
|
|
* by changing the palette and maybe tRNS when the first row was read!
|
|
*
|
|
* NOTE: PNG_FORMAT_FLAG_RANGE is lost at this point, even if the palette
|
|
* entries were shifted or inverted. This could be fixed, but it would
|
|
* complicate the libpng API to expose the information.
|
|
*/
|
|
/* Write the transformed palette: */
|
|
{
|
|
png_colorp palette = png_voidcast(png_colorp, png_calloc(png_ptr,
|
|
sizeof (png_color[PNG_MAX_PALETTE_LENGTH])));
|
|
png_const_bytep p;
|
|
const int is_color = (cp->tend.format & PNG_FORMAT_FLAG_COLOR) != 0;
|
|
unsigned int i;
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
unsigned int num_trans = 0;
|
|
const int do_trans = (cp->tend.format & PNG_FORMAT_FLAG_ALPHA) != 0;
|
|
png_byte trans_alpha[PNG_MAX_PALETTE_LENGTH];
|
|
# endif /* READ_tRNS */
|
|
|
|
memset(palette, 0xFFU, sizeof (png_color[PNG_MAX_PALETTE_LENGTH]));
|
|
png_free(png_ptr, png_ptr->palette);
|
|
png_ptr->palette = palette;
|
|
|
|
for (i=0, p=cache.b8; i<cp->tend.width; ++i)
|
|
{
|
|
if (is_color)
|
|
{
|
|
palette[i].red = *p++;
|
|
palette[i].green = *p++;
|
|
palette[i].blue = *p++;
|
|
}
|
|
|
|
else
|
|
palette[i].blue = palette[i].green = palette[i].red = *p++;
|
|
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
if (do_trans)
|
|
{
|
|
png_byte a = *p++;
|
|
trans_alpha[i] = a;
|
|
|
|
/* Strip opaque entries from the end: */
|
|
if (a < 0xFFU)
|
|
num_trans = i+1;
|
|
}
|
|
# endif /* READ_tRNS */
|
|
}
|
|
|
|
png_ptr->num_palette = png_check_bits(png_ptr, cp->tend.width, 9);
|
|
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
if (num_trans > 0)
|
|
{
|
|
png_bytep tRNS = png_voidcast(png_bytep, png_malloc(png_ptr,
|
|
PNG_MAX_PALETTE_LENGTH));
|
|
|
|
memset(tRNS, 0xFFU, PNG_MAX_PALETTE_LENGTH);
|
|
|
|
if (png_ptr->trans_alpha != NULL)
|
|
png_free(png_ptr, png_ptr->trans_alpha);
|
|
|
|
png_ptr->trans_alpha = tRNS;
|
|
|
|
memcpy(tRNS, trans_alpha, num_trans);
|
|
png_ptr->num_trans = png_check_bits(png_ptr, num_trans, 9);
|
|
}
|
|
# endif /* READ_tRNS */
|
|
}
|
|
|
|
/* NOTE: the caller sets cp->start to cp->end and cp->tend to cp->tstart,
|
|
* this causes processing to continue with the palette format and the
|
|
* first unprocessed transform. The reset of the transform control loses the
|
|
* gamma information as well, of course, as any information about the palette
|
|
* and tRNS changes (such as the RANGE flags).
|
|
*
|
|
* The following ensures that png_read_update_info knows to update the
|
|
* palette in png_info (which is no longer shared).
|
|
*/
|
|
png_ptr->palette_updated = 1U;
|
|
}
|
|
|
|
/* These structure and the save/restore routines that follow it exist to save
|
|
* data from a png_transform_control that is specific to the sample encoding of
|
|
* the PNG data, rather than the row format itself.
|
|
*/
|
|
typedef struct
|
|
{
|
|
# ifdef PNG_READ_GAMMA_SUPPORTED
|
|
png_fixed_point gamma;
|
|
# endif
|
|
png_byte sBIT_R;
|
|
png_byte sBIT_G;
|
|
png_byte sBIT_B;
|
|
png_byte sBIT_A; /* Signnificant bits in the row channels. */
|
|
unsigned int invalid_info; /* PNG_INFO_* for invalidated chunks */
|
|
} png_tc_channel_data;
|
|
|
|
static void
|
|
save_cp_channel_data(png_tc_channel_data *save, png_const_transform_controlp tc)
|
|
{
|
|
# ifdef PNG_READ_GAMMA_SUPPORTED
|
|
save->gamma = tc->gamma;
|
|
# endif /* READ_GAMMA */
|
|
|
|
/* The sBIT information and the list of invalidated chunks must also be
|
|
* preserved:
|
|
*/
|
|
save->sBIT_R = tc->sBIT_R;
|
|
save->sBIT_G = tc->sBIT_G;
|
|
save->sBIT_B = tc->sBIT_B;
|
|
save->sBIT_A = tc->sBIT_A;
|
|
save->invalid_info = tc->invalid_info;
|
|
}
|
|
|
|
static void
|
|
restore_cp_channel_data(png_transform_controlp tc,
|
|
const png_tc_channel_data *save)
|
|
/* Reverse the above */
|
|
{
|
|
# ifdef PNG_READ_GAMMA_SUPPORTED
|
|
tc->gamma = save->gamma;
|
|
# endif /* READ_GAMMA */
|
|
|
|
tc->sBIT_R = save->sBIT_R;
|
|
tc->sBIT_G = save->sBIT_G;
|
|
tc->sBIT_B = save->sBIT_B;
|
|
tc->sBIT_A = save->sBIT_A;
|
|
tc->invalid_info = save->invalid_info;
|
|
}
|
|
|
|
static void
|
|
make_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth)
|
|
{
|
|
/* At present the cache is just a byte lookup table. We need the original
|
|
* pixel depth to work out how big the working buffer needs to be.
|
|
*/
|
|
const unsigned int ipd = PNG_TC_PIXEL_DEPTH(cp->tstart);
|
|
const unsigned int opd = PNG_TC_PIXEL_DEPTH(cp->tend);
|
|
unsigned int order; /* records position of start transform */
|
|
unsigned int width; /* width of cache in pixels */
|
|
png_tc_channel_data save; /* Record of the final channel info */
|
|
union
|
|
{
|
|
png_uint_32 u32[1];
|
|
png_uint_16 u16[1]; /* For alignment */
|
|
png_byte b8[8*256]; /* For 16-bit RGBA */
|
|
} cache;
|
|
|
|
debug(cp->tend.init == PNG_TC_INIT_FINAL);
|
|
affirm(opd <= 64 && max_depth <= 64); /* or the cache is not big enough */
|
|
affirm(ipd == opd || (opd & 0x7U) == 0);
|
|
|
|
if ((cp->tstart.format & PNG_FORMAT_FLAG_COLORMAP) != 0)
|
|
width = setup_palette_cache(png_ptr, cache.b8);
|
|
|
|
else switch (ipd)
|
|
{
|
|
/* The input to the cache is the full range of possible pixel values: */
|
|
case 1:
|
|
/* 2 1-bit pixels, MSB first */
|
|
cache.b8[0] = 0x40U;
|
|
width = 2;
|
|
break;
|
|
|
|
case 2:
|
|
/* 4 2-bit pixels, MSB first */
|
|
cache.b8[0] = 0x1BU;
|
|
width = 4;
|
|
break;
|
|
|
|
case 4:
|
|
/* 16 4-bit pixels, MSB first */
|
|
cache.b8[0] = 0x01U;
|
|
cache.b8[1] = 0x23U;
|
|
cache.b8[2] = 0x45U;
|
|
cache.b8[3] = 0x67U;
|
|
cache.b8[4] = 0x89U;
|
|
cache.b8[5] = 0xABU;
|
|
cache.b8[6] = 0xCDU;
|
|
cache.b8[7] = 0xEFU;
|
|
width = 16;
|
|
break;
|
|
|
|
case 8:
|
|
/* 256 8-bit pixels */
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i=0; i<256; ++i)
|
|
cache.b8[i] = PNG_BYTE(i);
|
|
}
|
|
width = 256;
|
|
break;
|
|
|
|
default:
|
|
impossible("cache input bit depth");
|
|
}
|
|
|
|
/* Reset the transform control to run the transforms on this data, but save
|
|
* the channel info because the row processing functions do not always
|
|
* write it.
|
|
*/
|
|
save_cp_channel_data(&save, &cp->tend);
|
|
cp->tend = cp->tstart;
|
|
init_caching(png_ptr, cp);
|
|
/* And set tend to work out the result of transforming each possible pixel
|
|
* value:
|
|
*/
|
|
cp->tend.init = 0U;
|
|
cp->tend.width = width;
|
|
cp->tend.sp = cache.b8;
|
|
cp->tend.dp = cache.b8;
|
|
|
|
{
|
|
unsigned int check_depth =
|
|
png_run_this_transform_list_forwards(&cp->tend, cp->start, *cp->end);
|
|
|
|
/* This must not change: */
|
|
affirm(PNG_TC_PIXEL_DEPTH(cp->tend) == opd && check_depth == max_depth);
|
|
}
|
|
|
|
/* Restore the potentially lost channel data. */
|
|
restore_cp_channel_data(&cp->tend, &save);
|
|
|
|
/* This is all the information required to cache the set of transforms
|
|
* between 'start' and 'end'. We take the transformed pixels and make a
|
|
* cache transform of them. The cache transform skips the work, transforms
|
|
* the row, and sets the tranform_control to (a copy of) cp->tend.
|
|
*
|
|
* Remove all the transforms between start(inclusive) and end(exclusive);
|
|
* they have been processed. The effect they had on the transform control
|
|
* is irrelevant because the caller re-instates the settings from tstart.
|
|
*/
|
|
{
|
|
png_transformp list = *cp->start; /* list to free */
|
|
|
|
*cp->start = *cp->end; /* part of list not to be freed */
|
|
*cp->end = NULL; /* terminate the list to be freed */
|
|
cp->end = NULL; /* reset below */
|
|
|
|
order = list->order; /* used below when adding the cache transform */
|
|
png_transform_free(png_ptr, &list);
|
|
}
|
|
|
|
/* Make the required cache, as enumerated above there are 22 possibilities,
|
|
* this selects between them, fixes up the cache for the 'byte' cases (where
|
|
* multiple pixels can be handled byte-by-byte) and selects the correct
|
|
* transform function.
|
|
*/
|
|
if (ipd == opd)
|
|
{
|
|
/* We already know that ipd is <= 8 bits, so we can expand this case to
|
|
* the byte transform. The complexity is that for ipd < 8 bits we only
|
|
* have information for individual pixel values and these may be
|
|
* pixel-swapped within the byte.
|
|
*/
|
|
if (ipd < 8)
|
|
{
|
|
const int lsb = (cp->tend.format & PNG_FORMAT_FLAG_SWAPPED) != 0;
|
|
unsigned int ishift, b;
|
|
png_byte bcache[256];
|
|
|
|
switch (ipd)
|
|
{
|
|
case 1: ishift = 3U; break;
|
|
case 2: ishift = 2U; break;
|
|
case 4: ishift = 1U; break;
|
|
default: impossible("ipd");
|
|
}
|
|
|
|
/* Work out the right answer for each byte of pixels: */
|
|
for (b=0U; b<256U; ++b)
|
|
{
|
|
unsigned int o = 0U; /* output byte */
|
|
unsigned int p = 8U; /* right shift to find input pixel */
|
|
|
|
do
|
|
{
|
|
unsigned int q = ((1U<<ipd)-1U) & (b >> (p-=ipd));
|
|
/* The input pixel. For a palette this value might be outside
|
|
* the range of palette indices, in which case simply insert
|
|
* '0':
|
|
*/
|
|
if (q < width)
|
|
{
|
|
unsigned int r = cache.b8[q >> ishift];
|
|
r >>= ((lsb ? q : ~q) & ((1U<<ishift)-1U)) << (3U-ishift);
|
|
r &= ((1U<<ipd)-1U);
|
|
o |= r << (lsb ? (8U-ipd)-p : p);
|
|
}
|
|
|
|
else
|
|
{
|
|
UNTESTED
|
|
}
|
|
}
|
|
while (p != 0U);
|
|
|
|
bcache[b] = png_check_byte(png_ptr, o);
|
|
}
|
|
|
|
/* This is a byte transform, with the optional check-for-invalid-index
|
|
* functionality.
|
|
*/
|
|
add_cache_transform(png_ptr, order, do_transform_cache_byte, cp,
|
|
bcache, 256U);
|
|
}
|
|
|
|
else /* ipd == 8 */
|
|
add_cache_transform(png_ptr, order, do_transform_cache_byte, cp,
|
|
cache.b8, 256U);
|
|
}
|
|
|
|
else
|
|
{
|
|
/* opd is a whole number of bytes, ipd is 1, 2, 4 or 8 and not equal to
|
|
* opd.
|
|
*/
|
|
png_transform_fn fn;
|
|
|
|
# define C(ipd,opd) ((ipd) + 8*(opd))
|
|
switch (C(ipd,opd))
|
|
{
|
|
# define CASE(ipd,opd)\
|
|
case C(ipd,opd): fn = do_transform_cache(ipd,opd); break
|
|
|
|
CASE(1,8);
|
|
CASE(2,8);
|
|
CASE(4,8);
|
|
/* No 8,8 */
|
|
|
|
# define CASES(opd)\
|
|
CASE(1,opd);\
|
|
CASE(2,opd);\
|
|
CASE(4,opd);\
|
|
CASE(8,opd)
|
|
|
|
CASES(16);
|
|
CASES(24);
|
|
CASES(32);
|
|
CASES(48);
|
|
CASES(64);
|
|
# undef CASES
|
|
# undef CASE
|
|
|
|
default:
|
|
impossible("cache bit depths");
|
|
}
|
|
# undef C
|
|
|
|
/* In the event that the cache is not the full width implied by ipd zero
|
|
* the remaining bytes for security; otherwise they get copied into the
|
|
* cache transform and might get used. (Specifically if there is an
|
|
* out-of-range palette index they do get used!)
|
|
*/
|
|
{
|
|
unsigned int size = transform_cache_size(ipd, opd);
|
|
png_alloc_size_t cachebytes = PNG_TC_ROWBYTES(cp->tend);
|
|
|
|
affirm(cachebytes <= sizeof cache.b8);
|
|
|
|
if (cachebytes < size)
|
|
memset(cache.b8+cachebytes, 0, size - cachebytes);
|
|
|
|
add_cache_transform(png_ptr, order, fn, cp, cache.b8, size);
|
|
}
|
|
}
|
|
|
|
/* Because a transform was inserted cp->end needs to be set to the new
|
|
* pointer to the original end. add_cache_transform sets cp->start to this,
|
|
* so:
|
|
*/
|
|
cp->end = cp->start;
|
|
|
|
/* This invalidates the palette if that is what was cached because the
|
|
* palette and, if present, tRNS chunk did not get updated above.
|
|
*/
|
|
if (cp->tstart.palette)
|
|
png_remove_PLTE_and_tRNS(png_ptr);
|
|
}
|
|
|
|
static void restore_cp(png_cache_paramsp cp)
|
|
{
|
|
/* A utility to restore cp->tstart by copying it into cp->tend. This is used
|
|
* both in the palette case when restoring the transform control for the
|
|
* indexed data and in the case where no transforms were cached. It
|
|
* preserves the color-channel-specific data from cp->tend because in either
|
|
* case it is possible for this data to be modified without preserving any
|
|
* transforms, e.g. if only the gamma is changed but no gamma transform is
|
|
* retained because the change was not significant.
|
|
*/
|
|
png_tc_channel_data save;
|
|
|
|
save_cp_channel_data(&save, &cp->tend);
|
|
cp->tend = cp->tstart;
|
|
restore_cp_channel_data(&cp->tend, &save);
|
|
}
|
|
|
|
static void
|
|
handle_cache(png_structp png_ptr, png_cache_paramsp cp, unsigned int max_depth)
|
|
{
|
|
/* There is nothing to do if there are no transforms between 'start' and
|
|
* 'end':
|
|
*/
|
|
if (cp->start != cp->end)
|
|
{
|
|
only_deb(png_transformp tr_check = *cp->end;)
|
|
|
|
/* libpng doesn't currently implement any pixel size of more than 64 bits
|
|
* so:
|
|
*/
|
|
affirm(max_depth <= 64);
|
|
|
|
if (cp->tend.palette)
|
|
{
|
|
/* The transforms being cached apply to the palette, the following
|
|
* transforms will apply to the original index data and the transformed
|
|
* data must be used to update the palette:
|
|
*/
|
|
if (cp->tend.init == PNG_TC_INIT_FINAL)
|
|
update_palette(png_ptr, cp, max_depth);
|
|
|
|
cp->start = cp->end;
|
|
restore_cp(cp); /* reset to palette data */
|
|
}
|
|
|
|
else
|
|
{
|
|
/* Continue with the transform control in cp.tend; even if there was
|
|
* palette data in cp.tstart it has been expanded.
|
|
*/
|
|
if (cp->tend.init == PNG_TC_INIT_FINAL)
|
|
make_cache(png_ptr, cp, max_depth);
|
|
|
|
cp->tstart = cp->tend; /* keep current context */
|
|
}
|
|
|
|
debug(tr_check == *cp->end);
|
|
}
|
|
|
|
else /* no transforms cached */
|
|
restore_cp(cp); /* removes any palette caching info */
|
|
}
|
|
|
|
#ifdef PNG_READ_tRNS_SUPPORTED
|
|
static void
|
|
check_tRNS_for_alpha(png_structrp png_ptr)
|
|
{
|
|
unsigned int num_trans = png_ptr->num_trans;
|
|
|
|
debug(png_ptr->color_type == PNG_COLOR_TYPE_PALETTE);
|
|
|
|
while (num_trans > 0)
|
|
{
|
|
{
|
|
const png_byte trans = png_ptr->trans_alpha[--num_trans];
|
|
|
|
if (trans == 0xFFU)
|
|
continue;
|
|
|
|
if (trans > 0U)
|
|
return; /* Palette has at least one entry >0, <0xff */
|
|
}
|
|
|
|
/* There is some point to the tRNS chunk; it has a non-opaque entry, this
|
|
* code could truncate it but there is no obvious performance advantage to
|
|
* doing this.
|
|
*/
|
|
while (num_trans > 0)
|
|
{
|
|
const png_byte trans = png_ptr->trans_alpha[--num_trans];
|
|
|
|
if (trans > 0U && trans < 0xFFU)
|
|
return;
|
|
}
|
|
|
|
/* Here if the above did not find an entry >0 && <0xFFU but did find a
|
|
* transparent entry (0u). Record this.
|
|
*/
|
|
png_ptr->transparent_palette = 1U;
|
|
return;
|
|
}
|
|
|
|
/* All entries opaque; remove the tRNS data: */
|
|
png_ptr->num_trans = 0U;
|
|
}
|
|
#endif /* READ_tRNS */
|
|
|
|
unsigned int /* PRIVATE */
|
|
png_read_init_transform_mech(png_structp png_ptr, png_transform_controlp tc)
|
|
/* This is called once for each init stage (PNG_TC_INIT_FORMAT and
|
|
* PNG_TC_INIT_FINAL) to run the transform list forwards, returning the
|
|
* maximum depth required to process the row. It handles caching of the
|
|
* transforms and the processing of the palette for color-mapped PNG data.
|
|
*/
|
|
{
|
|
png_transformp *list = &png_ptr->transform_list;
|
|
unsigned int max_depth;
|
|
png_cache_params cp;
|
|
|
|
/* PNG color-mapped data must be handled here so that the palette is updated
|
|
* correctly. png_set_palette_to_rgb causes the palette flag to be removed
|
|
* from the transform control but does no other change. png_set_quantize
|
|
* causes 8-bit RGB, RGBA or palette data to be converted into palette
|
|
* indices, setting the palette flag.
|
|
*/
|
|
# ifdef PNG_READ_tRNS_SUPPORTED
|
|
/* This happens once at the start to find out if the tRNS chunk consisted
|
|
* entirely of opaque (255) and/or transparent (0) entries.
|
|
*/
|
|
if (tc->init == PNG_TC_INIT_FORMAT &&
|
|
png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
|
|
check_tRNS_for_alpha(png_ptr);
|
|
# endif /* READ_tRNS */
|
|
cp.end = cp.start = list;
|
|
cp.tend = cp.tstart = *tc;
|
|
init_caching(png_ptr, &cp);
|
|
max_depth = PNG_TC_PIXEL_DEPTH(cp.tend);
|
|
|
|
while (*cp.end != NULL)
|
|
{
|
|
png_transformp tr = *cp.end;
|
|
|
|
/* The user transform cannot be cached. */
|
|
if (tr->order >= PNG_TR_USER)
|
|
break;
|
|
|
|
/* If the 'palette' flag is set and the next transform has order
|
|
* PNG_TR_ENCODING or later cache the results so far and continue with the
|
|
* original palette data (cp.tstart).
|
|
*/
|
|
if (cp.tend.palette && tr->order >= PNG_TR_ENCODING)
|
|
{
|
|
handle_cache(png_ptr, &cp, max_depth);
|
|
|
|
/* The cache handling function must maintain cp.end; */
|
|
affirm(tr == *cp.end);
|
|
max_depth = PNG_TC_PIXEL_DEPTH(cp.tend);
|
|
}
|
|
|
|
/* Now run the transform list entry: */
|
|
if (tr->fn != NULL)
|
|
{
|
|
tr->fn(cp.end, &cp.tend);
|
|
tr = *cp.end; /* in case something was inserted */
|
|
}
|
|
|
|
if (tr->fn == NULL) /* delete this transform */
|
|
png_remove_transform(png_ptr, cp.end);
|
|
|
|
else
|
|
{
|
|
/* Handle the initialization of the maximum pixel depth. */
|
|
unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(cp.tend);
|
|
|
|
if (tc_depth > max_depth)
|
|
max_depth = tc_depth;
|
|
|
|
/* Advance to the next transform. */
|
|
cp.end = &tr->next;
|
|
}
|
|
}
|
|
|
|
/* At the end if still caching record the cache information (this is common;
|
|
* this is generally the case for an expanded palette.)
|
|
*/
|
|
if (cp.tend.caching)
|
|
{
|
|
png_transformp tr = *cp.end;
|
|
handle_cache(png_ptr, &cp, max_depth);
|
|
affirm(tr == *cp.end);
|
|
max_depth = PNG_TC_PIXEL_DEPTH(cp.tend);
|
|
}
|
|
|
|
/* At the end run the init on the user transform: */
|
|
if (*cp.end != NULL)
|
|
{
|
|
png_transformp tr = *cp.end;
|
|
affirm(tr->order == PNG_TR_USER);
|
|
if (tr->fn != NULL)
|
|
tr->fn(cp.end, &cp.tend);
|
|
/* This cannot insert anything, so: */
|
|
affirm(tr == *cp.end && tr->next == NULL);
|
|
|
|
if (tr->fn == NULL) /* delete this transform */
|
|
png_remove_transform(png_ptr, cp.end);
|
|
|
|
else
|
|
{
|
|
unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(cp.tend);
|
|
|
|
if (tc_depth > max_depth)
|
|
max_depth = tc_depth;
|
|
}
|
|
}
|
|
|
|
/* And write the input transform control: */
|
|
*tc = cp.tend;
|
|
|
|
return max_depth;
|
|
}
|
|
|
|
/* Modify the info structure to reflect the transformations. The
|
|
* info should be updated so a PNG file could be written with it,
|
|
* assuming the transformations result in valid PNG data.
|
|
*/
|
|
void /* PRIVATE */
|
|
png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr)
|
|
{
|
|
png_debug(1, "in png_read_transform_info");
|
|
|
|
/* WARNING: this is very basic at present. It just updates the format
|
|
* information. It should update the palette (and will eventually) as well
|
|
* as invalidating chunks that the transforms break.
|
|
*/
|
|
# ifdef PNG_TRANSFORM_MECH_SUPPORTED
|
|
info_ptr->format = png_ptr->row_format;
|
|
info_ptr->bit_depth = png_ptr->row_bit_depth;
|
|
# ifdef PNG_READ_GAMMA_SUPPORTED
|
|
/* If an info struct is used with a different png_ptr in a call to
|
|
* png_set_gAMA then the png_struct information won't be updated, this
|
|
* doesn't matter on write, but don't zap the value in the info on read
|
|
* unless it is known:
|
|
*
|
|
* TODO: review this whole mess.
|
|
*/
|
|
if (png_ptr->row_gamma > 0)
|
|
info_ptr->colorspace.gamma = png_ptr->row_gamma;
|
|
# endif
|
|
|
|
/* Invalidate chunks marked as invalid: */
|
|
# ifdef PNG_READ_TRANSFORMS_SUPPORTED
|
|
info_ptr->valid &= ~png_ptr->invalid_info;
|
|
|
|
/* If the palette or tRNS chunk was changed copy them over to the info
|
|
* structure; this may actually re-validate the PLTE or tRNS chunks,
|
|
* but only if png_ptr has a new version, otherwise the invalid_info
|
|
* settings from above can still invalidate the chunk.
|
|
*/
|
|
if (png_ptr->palette_updated)
|
|
{
|
|
if (png_ptr->num_palette > 0)
|
|
png_set_PLTE(png_ptr, info_ptr, png_ptr->palette,
|
|
png_ptr->num_palette);
|
|
|
|
else
|
|
{
|
|
png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);
|
|
info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_PLTE);
|
|
}
|
|
|
|
# ifdef PNG_READ_tRNS
|
|
/* If the output format is not a palette format the tRNS
|
|
* information was a single color which is now invalid
|
|
* (probably), otherwise the array of tRNS values must be
|
|
* updated.
|
|
*/
|
|
if ((info_ptr->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
|
|
{
|
|
if (png_ptr->num_trans > 0)
|
|
png_set_tRNS(png_ptr, info_ptr, png_ptr->trans_alpha,
|
|
png_ptr->num_trans, NULL/*trans color*/);
|
|
|
|
else
|
|
{
|
|
png_free_data(png_ptr, info_ptr, PNG_FREE_tRNS, 0);
|
|
info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS);
|
|
}
|
|
}
|
|
|
|
else
|
|
info_ptr->valid &= PNG_BIC_MASK(PNG_INFO_tRNS);
|
|
# endif /* READ_tRNS */
|
|
}
|
|
# endif /* READ_TRANSFORMS */
|
|
# else /* !TRANSFORM_MECH */
|
|
PNG_UNUSED(png_ptr)
|
|
PNG_UNUSED(info_ptr)
|
|
# endif /* !TRANSFORM_MECH */
|
|
}
|
|
#endif /* READ_TRANSFORMS */
|