Mirror of roytam1's UXP fork just in case Moonchild and Tobin decide to go after him
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5006 lines
165 KiB

/* pngrtran.c - transforms the data in a row for PNG readers
*
* Last changed in libpng 1.6.24 [August 4, 2016]
* 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"
#ifdef PNG_READ_SUPPORTED
/* Set the action on getting a CRC error for an ancillary or critical chunk. */
void PNGAPI
png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action)
{
png_debug(1, "in png_set_crc_action");
if (png_ptr == NULL)
return;
/* Tell libpng how we react to CRC errors in critical chunks */
switch (crit_action)
{
case PNG_CRC_NO_CHANGE: /* Leave setting as is */
break;
case PNG_CRC_WARN_USE: /* Warn/use data */
png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE;
break;
case PNG_CRC_QUIET_USE: /* Quiet/use data */
png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE |
PNG_FLAG_CRC_CRITICAL_IGNORE;
break;
case PNG_CRC_WARN_DISCARD: /* Not a valid action for critical data */
png_warning(png_ptr,
"Can't discard critical data on CRC error");
case PNG_CRC_ERROR_QUIT: /* Error/quit */
case PNG_CRC_DEFAULT:
default:
png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
break;
}
/* Tell libpng how we react to CRC errors in ancillary chunks */
switch (ancil_action)
{
case PNG_CRC_NO_CHANGE: /* Leave setting as is */
break;
case PNG_CRC_WARN_USE: /* Warn/use data */
png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE;
break;
case PNG_CRC_QUIET_USE: /* Quiet/use data */
png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE |
PNG_FLAG_CRC_ANCILLARY_NOWARN;
break;
case PNG_CRC_ERROR_QUIT: /* Error/quit */
png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN;
break;
case PNG_CRC_WARN_DISCARD: /* Warn/discard data */
case PNG_CRC_DEFAULT:
default:
png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
break;
}
}
#ifdef PNG_READ_TRANSFORMS_SUPPORTED
/* Is it OK to set a transformation now? Only if png_start_read_image or
* png_read_update_info have not been called. It is not necessary for the IHDR
* to have been read in all cases; the need_IHDR parameter allows for this
* check too.
*/
static int
png_rtran_ok(png_structrp png_ptr, int need_IHDR)
{
if (png_ptr != NULL)
{
if ((png_ptr->flags & PNG_FLAG_ROW_INIT) != 0)
png_app_error(png_ptr,
"invalid after png_start_read_image or png_read_update_info");
else if (need_IHDR && (png_ptr->mode & PNG_HAVE_IHDR) == 0)
png_app_error(png_ptr, "invalid before the PNG header has been read");
else
{
/* Turn on failure to initialize correctly for all transforms. */
png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED;
return 1; /* Ok */
}
}
return 0; /* no png_error possible! */
}
#endif
#ifdef PNG_READ_BACKGROUND_SUPPORTED
/* Handle alpha and tRNS via a background color */
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)
{
png_debug(1, "in png_set_background_fixed");
if (png_rtran_ok(png_ptr, 0) == 0 || background_color == NULL)
return;
if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN)
{
png_warning(png_ptr, "Application must supply a known background gamma");
return;
}
png_ptr->transformations |= PNG_COMPOSE | PNG_STRIP_ALPHA;
png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
png_ptr->background = *background_color;
png_ptr->background_gamma = background_gamma;
png_ptr->background_gamma_type = (png_byte)(background_gamma_code);
if (need_expand != 0)
png_ptr->transformations |= PNG_BACKGROUND_EXPAND;
else
png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND;
}
# 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 */
/* Scale 16-bit depth files to 8-bit depth. If both of these are set then the
* one that pngrtran does first (scale) happens. This is necessary to allow the
* TRANSFORM and API behavior to be somewhat consistent, and it's simpler.
*/
#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
void PNGAPI
png_set_scale_16(png_structrp png_ptr)
{
png_debug(1, "in png_set_scale_16");
if (png_rtran_ok(png_ptr, 0) == 0)
return;
png_ptr->transformations |= PNG_SCALE_16_TO_8;
}
#endif
#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
/* Chop 16-bit depth files to 8-bit depth */
void PNGAPI
png_set_strip_16(png_structrp png_ptr)
{
png_debug(1, "in png_set_strip_16");
if (png_rtran_ok(png_ptr, 0) == 0)
return;
png_ptr->transformations |= PNG_16_TO_8;
}
#endif
#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
void PNGAPI
png_set_strip_alpha(png_structrp png_ptr)
{
png_debug(1, "in png_set_strip_alpha");
if (png_rtran_ok(png_ptr, 0) == 0)
return;
png_ptr->transformations |= PNG_STRIP_ALPHA;
}
#endif
#if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED)
static png_fixed_point
translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma,
int is_screen)
{
/* 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!
*/
if (output_gamma == PNG_DEFAULT_sRGB ||
output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB)
{
/* If there is no sRGB support this just sets the gamma to the standard
* sRGB value. (This is a side effect of using this function!)
*/
# ifdef PNG_READ_sRGB_SUPPORTED
png_ptr->flags |= PNG_FLAG_ASSUME_sRGB;
# else
PNG_UNUSED(png_ptr)
# endif
if (is_screen != 0)
output_gamma = PNG_GAMMA_sRGB;
else
output_gamma = PNG_GAMMA_sRGB_INVERSE;
}
else if (output_gamma == PNG_GAMMA_MAC_18 ||
output_gamma == PNG_FP_1 / PNG_GAMMA_MAC_18)
{
if (is_screen != 0)
output_gamma = PNG_GAMMA_MAC_OLD;
else
output_gamma = PNG_GAMMA_MAC_INVERSE;
}
return output_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 *= PNG_FP_1;
/* This preserves -1 and -2 exactly: */
output_gamma = floor(output_gamma + .5);
if (output_gamma > PNG_FP_MAX || output_gamma < PNG_FP_MIN)
png_fixed_error(png_ptr, "gamma value");
return (png_fixed_point)output_gamma;
}
# endif
#endif /* READ_ALPHA_MODE || READ_GAMMA */
#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
void PNGFAPI
png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,
png_fixed_point output_gamma)
{
int compose = 0;
png_fixed_point file_gamma;
png_debug(1, "in png_set_alpha_mode");
if (png_rtran_ok(png_ptr, 0) == 0)
return;
output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/);
/* Validate the value to ensure it is in a reasonable range. The value
* is expected to be 1 or greater, but this range test allows for some
* viewing correction values. The intent is to weed out users of this API
* who use the inverse of the gamma value accidentally! Since some of these
* values are reasonable this may have to be changed:
*
* 1.6.x: changed from 0.07..3 to 0.01..100 (to accomodate the optimal 16-bit
* gamma of 36, and its reciprocal.)
*/
if (output_gamma < 1000 || output_gamma > 10000000)
png_error(png_ptr, "output gamma out of expected range");
/* The default file gamma is the inverse of the output gamma; the output
* gamma may be changed below so get the file value first:
*/
file_gamma = png_reciprocal(output_gamma);
/* There are really 8 possibilities here, composed of any combination
* of:
*
* premultiply the color channels
* do not encode non-opaque pixels
* 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.
*
* png_set_alpha_mode and png_set_background interact because both use
* png_compose to do the work. Calling both is only useful when
* png_set_alpha_mode is used to set the default mode - PNG_ALPHA_PNG - along
* with a default gamma value. Otherwise PNG_COMPOSE must not be set.
*/
switch (mode)
{
case PNG_ALPHA_PNG: /* default: png standard */
/* No compose, but it may be set by png_set_background! */
png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
break;
case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */
compose = 1;
png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
/* The output is linear: */
output_gamma = PNG_FP_1;
break;
case PNG_ALPHA_OPTIMIZED: /* associated, non-opaque pixels linear */
compose = 1;
png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
png_ptr->flags |= PNG_FLAG_OPTIMIZE_ALPHA;
/* output_gamma records the encoding of opaque pixels! */
break;
case PNG_ALPHA_BROKEN: /* associated, non-linear, alpha encoded */
compose = 1;
png_ptr->transformations |= PNG_ENCODE_ALPHA;
png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
break;
default:
png_error(png_ptr, "invalid alpha mode");
}
/* Only set the default gamma if the file gamma has not 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.gamma == 0)
{
png_ptr->colorspace.gamma = file_gamma;
png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
}
/* But always set the output gamma: */
png_ptr->screen_gamma = output_gamma;
/* Finally, if pre-multiplying, set the background fields to achieve the
* desired result.
*/
if (compose != 0)
{
/* And obtain alpha pre-multiplication by composing on black: */
memset(&png_ptr->background, 0, (sizeof png_ptr->background));
png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */
png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE;
png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND;
if ((png_ptr->transformations & PNG_COMPOSE) != 0)
png_error(png_ptr,
"conflicting calls to set alpha mode and background");
png_ptr->transformations |= PNG_COMPOSE;
}
}
# 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
#endif
#ifdef PNG_READ_QUANTIZE_SUPPORTED
/* 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 than 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;
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_rtran_ok(png_ptr, 0) == 0)
return;
png_ptr->transformations |= PNG_QUANTIZE;
if (full_quantize == 0)
{
int i;
png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr,
(png_uint_32)((png_uint_32)num_palette * (sizeof (png_byte))));
for (i = 0; i < num_palette; i++)
png_ptr->quantize_index[i] = (png_byte)i;
}
if (num_palette > maximum_colors)
{
if (histogram != NULL)
{
/* This is easy enough, just throw out the least used colors.
* Perhaps not the best solution, but good enough.
*/
int i;
/* Initialize an array to sort colors */
png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr,
(png_uint_32)((png_uint_32)num_palette * (sizeof (png_byte))));
/* Initialize the quantize_sort array */
for (i = 0; i < num_palette; i++)
png_ptr->quantize_sort[i] = (png_byte)i;
/* 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[png_ptr->quantize_sort[j]]
< histogram[png_ptr->quantize_sort[j + 1]])
{
png_byte t;
t = png_ptr->quantize_sort[j];
png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1];
png_ptr->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 != 0)
{
int j = num_palette;
/* Put all the useful colors within the max, but don't
* move the others.
*/
for (i = 0; i < maximum_colors; i++)
{
if ((int)png_ptr->quantize_sort[i] >= maximum_colors)
{
do
j--;
while ((int)png_ptr->quantize_sort[j] >= maximum_colors);
palette[i] = palette[j];
}
}
}
else
{
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 ((int)png_ptr->quantize_sort[i] >= maximum_colors)
{
png_color tmp_color;
do
j--;
while ((int)png_ptr->quantize_sort[j] >= maximum_colors);
tmp_color = palette[j];
palette[j] = palette[i];
palette[i] = tmp_color;
/* Indicate where the color went */
png_ptr->quantize_index[j] = (png_byte)i;
png_ptr->quantize_index[i] = (png_byte)j;
}
}
/* Find closest color for those colors we are not using */
for (i = 0; i < num_palette; i++)
{
if ((int)png_ptr->quantize_index[i] >= maximum_colors)
{
int min_d, k, min_k, d_index;
/* Find the closest color to one we threw out */
d_index = png_ptr->quantize_index[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 */
png_ptr->quantize_index[i] = (png_byte)min_k;
}
}
}
png_free(png_ptr, png_ptr->quantize_sort);
png_ptr->quantize_sort = NULL;
}
else
{
/* 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 i;
int max_d;
int num_new_palette;
png_dsortp t;
png_dsortpp hash;
t = NULL;
/* Initialize palette index arrays */
png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr,
(png_uint_32)((png_uint_32)num_palette * (sizeof (png_byte))));
png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr,
(png_uint_32)((png_uint_32)num_palette * (sizeof (png_byte))));
/* Initialize the sort array */
for (i = 0; i < num_palette; i++)
{
png_ptr->index_to_palette[i] = (png_byte)i;
png_ptr->palette_to_index[i] = (png_byte)i;
}
hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 *
(sizeof (png_dsortp))));
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)
{
for (i = 0; i < num_new_palette - 1; i++)
{
int j;
for (j = i + 1; j < num_new_palette; j++)
{
int d;
d = PNG_COLOR_DIST(palette[i], palette[j]);
if (d <= max_d)
{
t = (png_dsortp)png_malloc_warn(png_ptr,
(png_uint_32)(sizeof (png_dsort)));
if (t == NULL)
break;
t->next = hash[d];
t->left = (png_byte)i;
t->right = (png_byte)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; p = p->next)
{
if ((int)png_ptr->index_to_palette[p->left]
< num_new_palette &&
(int)png_ptr->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--;
palette[png_ptr->index_to_palette[j]]
= palette[num_new_palette];
if (full_quantize == 0)
{
int k;
for (k = 0; k < num_palette; k++)
{
if (png_ptr->quantize_index[k] ==
png_ptr->index_to_palette[j])
png_ptr->quantize_index[k] =
png_ptr->index_to_palette[next_j];
if ((int)png_ptr->quantize_index[k] ==
num_new_palette)
png_ptr->quantize_index[k] =
png_ptr->index_to_palette[j];
}
}
png_ptr->index_to_palette[png_ptr->palette_to_index
[num_new_palette]] = png_ptr->index_to_palette[j];
png_ptr->palette_to_index[png_ptr->index_to_palette[j]]
= png_ptr->palette_to_index[num_new_palette];
png_ptr->index_to_palette[j] =
(png_byte)num_new_palette;
png_ptr->palette_to_index[num_new_palette] =
(png_byte)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] = 0;
}
max_d += 96;
}
png_free(png_ptr, hash);
png_free(png_ptr, png_ptr->palette_to_index);
png_free(png_ptr, png_ptr->index_to_palette);
png_ptr->palette_to_index = NULL;
png_ptr->index_to_palette = NULL;
}
num_palette = maximum_colors;
}
if (png_ptr->palette == NULL)
{
png_ptr->palette = palette;
}
png_ptr->num_palette = (png_uint_16)num_palette;
if (full_quantize != 0)
{
int i;
png_bytep distance;
int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS +
PNG_QUANTIZE_BLUE_BITS;
int num_red = (1 << PNG_QUANTIZE_RED_BITS);
int num_green = (1 << PNG_QUANTIZE_GREEN_BITS);
int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS);
png_size_t num_entries = ((png_size_t)1 << total_bits);
png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr,
(png_uint_32)(num_entries * (sizeof (png_byte))));
distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries *
(sizeof (png_byte))));
memset(distance, 0xff, num_entries * (sizeof (png_byte)));
for (i = 0; i < num_palette; i++)
{
int ir, ig, ib;
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 < num_red; ir++)
{
/* int dr = abs(ir - r); */
int dr = ((ir > r) ? ir - r : r - ir);
int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS +
PNG_QUANTIZE_GREEN_BITS));
for (ig = 0; ig < num_green; ig++)
{
/* int dg = abs(ig - g); */
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 < num_blue; 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 < (int)distance[d_index])
{
distance[d_index] = (png_byte)d;
png_ptr->palette_lookup[d_index] = (png_byte)i;
}
}
}
}
}
png_free(png_ptr, distance);
}
}
#endif /* READ_QUANTIZE */
#ifdef PNG_READ_GAMMA_SUPPORTED
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");
if (png_rtran_ok(png_ptr, 0) == 0)
return;
/* New in libpng-1.5.4 - reserve particular negative values as flags. */
scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/);
file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/);
/* Checking the gamma values for being >0 was added in 1.5.4 along with the
* premultiplied alpha support; this actually hides an undocumented feature
* of the previous implementation which allowed gamma processing to be
* disabled in background handling. There is no evidence (so far) that this
* was being used; however, png_set_background itself accepted and must still
* accept '0' for the gamma value it takes, because it isn't always used.
*
* Since this is an API change (albeit a very minor one that removes an
* undocumented API feature) the following checks were only enabled in
* libpng-1.6.0.
*/
if (file_gamma <= 0)
png_error(png_ptr, "invalid file gamma in png_set_gamma");
if (scrn_gamma <= 0)
png_error(png_ptr, "invalid screen gamma in png_set_gamma");
/* Set the gamma values 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;
png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
png_ptr->screen_gamma = scrn_gamma;
}
# ifdef PNG_FLOATING_POINT_SUPPORTED
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_EXPAND_SUPPORTED
/* 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.
*/
void PNGAPI
png_set_expand(png_structrp png_ptr)
{
png_debug(1, "in png_set_expand");
if (png_rtran_ok(png_ptr, 0) == 0)
return;
png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
}
/* GRR 19990627: the following three functions currently are identical
* to png_set_expand(). However, it is entirely reasonable that someone
* might wish to expand an indexed image to RGB but *not* expand a single,
* fully transparent palette entry to a full alpha channel--perhaps instead
* convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace
* the transparent color with a particular RGB value, or drop tRNS entirely.
* IOW, a future version of the library may make the transformations flag
* a bit more fine-grained, with separate bits for each of these three
* functions.
*
* More to the point, these functions make it obvious what libpng will be
* doing, whereas "expand" can (and does) mean any number of things.
*
* GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified
* to expand only the sample depth but not to expand the tRNS to alpha
* and its name was changed to png_set_expand_gray_1_2_4_to_8().
*/
/* Expand paletted images to RGB. */
void PNGAPI
png_set_palette_to_rgb(png_structrp png_ptr)
{
png_debug(1, "in png_set_palette_to_rgb");
if (png_rtran_ok(png_ptr, 0) == 0)
return;
png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
}
/* Expand grayscale images of less than 8-bit depth to 8 bits. */
void PNGAPI
png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr)
{
png_debug(1, "in png_set_expand_gray_1_2_4_to_8");
if (png_rtran_ok(png_ptr, 0) == 0)
return;
png_ptr->transformations |= PNG_EXPAND;
}
/* Expand tRNS chunks to alpha channels. */
void PNGAPI
png_set_tRNS_to_alpha(png_structrp png_ptr)
{
png_debug(1, "in png_set_tRNS_to_alpha");
if (png_rtran_ok(png_ptr, 0) == 0)
return;
png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
}
#endif /* READ_EXPAND */
#ifdef PNG_READ_EXPAND_16_SUPPORTED
/* Expand to 16-bit channels, expand the tRNS chunk too (because otherwise
* it may not work correctly.)
*/
void PNGAPI
png_set_expand_16(png_structrp png_ptr)
{
png_debug(1, "in png_set_expand_16");
if (png_rtran_ok(png_ptr, 0) == 0)
return;
png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS);
}
#endif
#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
void PNGAPI
png_set_gray_to_rgb(png_structrp png_ptr)
{
png_debug(1, "in png_set_gray_to_rgb");
if (png_rtran_ok(png_ptr, 0) == 0)
return;
/* Because rgb must be 8 bits or more: */
png_set_expand_gray_1_2_4_to_8(png_ptr);
png_ptr->transformations |= PNG_GRAY_TO_RGB;
}
#endif
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
void PNGFAPI
png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action,
png_fixed_point red, png_fixed_point green)
{
png_debug(1, "in png_set_rgb_to_gray");
/* Need the IHDR here because of the check on color_type below. */
/* TODO: fix this */
if (png_rtran_ok(png_ptr, 1) == 0)
return;
switch (error_action)
{
case PNG_ERROR_ACTION_NONE:
png_ptr->transformations |= PNG_RGB_TO_GRAY;
break;
case PNG_ERROR_ACTION_WARN:
png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN;
break;
case PNG_ERROR_ACTION_ERROR:
png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR;
break;
default:
png_error(png_ptr, "invalid error action to rgb_to_gray");
}
if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
#ifdef PNG_READ_EXPAND_SUPPORTED
png_ptr->transformations |= PNG_EXPAND;
#else
{
/* Make this an error in 1.6 because otherwise the application may assume
* that it just worked and get a memory overwrite.
*/
png_error(png_ptr,
"Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED");
/* png_ptr->transformations &= ~PNG_RGB_TO_GRAY; */
}
#endif
{
if (red >= 0 && green >= 0 && red + green <= PNG_FP_1)
{
png_uint_16 red_int, green_int;
/* NOTE: this calculation does not round, but this behavior is retained
* for consistency; the inaccuracy is very small. The code here always
* overwrites the coefficients, regardless of whether they have been
* defaulted or set already.
*/
red_int = (png_uint_16)(((png_uint_32)red*32768)/100000);
green_int = (png_uint_16)(((png_uint_32)green*32768)/100000);
png_ptr->rgb_to_gray_red_coeff = red_int;
png_ptr->rgb_to_gray_green_coeff = green_int;
png_ptr->rgb_to_gray_coefficients_set = 1;
}
else
{
if (red >= 0 && green >= 0)
png_app_warning(png_ptr,
"ignoring out of range rgb_to_gray coefficients");
/* Use the defaults, from the cHRM chunk if set, else the historical
* values which are close to the sRGB/HDTV/ITU-Rec 709 values. See
* png_do_rgb_to_gray for more discussion of the values. In this case
* the coefficients are not marked as 'set' and are not overwritten if
* something has already provided a default.
*/
if (png_ptr->rgb_to_gray_red_coeff == 0 &&
png_ptr->rgb_to_gray_green_coeff == 0)
{
png_ptr->rgb_to_gray_red_coeff = 6968;
png_ptr->rgb_to_gray_green_coeff = 23434;
/* png_ptr->rgb_to_gray_blue_coeff = 2366; */
}
}
}
}
#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 */
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
void PNGAPI
png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
read_user_transform_fn)
{
png_debug(1, "in png_set_read_user_transform_fn");
#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
png_ptr->transformations |= PNG_USER_TRANSFORM;
png_ptr->read_user_transform_fn = read_user_transform_fn;
#endif
}
#endif
#ifdef PNG_READ_TRANSFORMS_SUPPORTED
#ifdef PNG_READ_GAMMA_SUPPORTED
/* In the case of gamma transformations only do transformations on images where
* the [file] gamma and screen_gamma are not close reciprocals, otherwise it
* slows things down slightly, and also needlessly introduces small errors.
*/
static int /* PRIVATE */
png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma)
{
/* PNG_GAMMA_THRESHOLD is the threshold for performing gamma
* correction as a difference of the overall transform from 1.0
*
* We want to compare the threshold with s*f - 1, if we get
* overflow here it is because of wacky gamma values so we
* turn on processing anyway.
*/
png_fixed_point gtest;
return !png_muldiv(&gtest, screen_gamma, file_gamma, PNG_FP_1) ||
png_gamma_significant(gtest);
}
#endif
/* Initialize everything needed for the read. This includes modifying
* the palette.
*/
/* For the moment 'png_init_palette_transformations' and
* 'png_init_rgb_transformations' only do some flag canceling optimizations.
* The intent is that these two routines should have palette or rgb operations
* extracted from 'png_init_read_transformations'.
*/
static void /* PRIVATE */
png_init_palette_transformations(png_structrp png_ptr)
{
/* Called to handle the (input) palette case. In png_do_read_transformations
* the first step is to expand the palette if requested, so this code must
* take care to only make changes that are invariant with respect to the
* palette expansion, or only do them if there is no expansion.
*
* STRIP_ALPHA has already been handled in the caller (by setting num_trans
* to 0.)
*/
int input_has_alpha = 0;
int input_has_transparency = 0;