/* pngrtran.c - transforms the data in a row for PNG readers * * Copyright (c) 2018-2024 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * 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_ARM_NEON_IMPLEMENTATION # if PNG_ARM_NEON_IMPLEMENTATION == 1 # define PNG_ARM_NEON_INTRINSICS_AVAILABLE # ifdefined(_MSC_VER) && !defined(__clang__) && defined(_M_ARM64) # include <arm64_neon.h> # else # include <arm_neon.h> # endif # endif #endif
#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"); /* FALLTHROUGH */ 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_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.
*/ staticint
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");
elseif (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;
}
/* 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");
#ifdefined(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;
}
# 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);
/* 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 the API users * who might use the inverse of the gamma value accidentally! * * In libpng 1.6.0, we changed from 0.07..3 to 0.01..100, to accommodate * 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;
/* 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");
#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.
*/
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_alloc_size_t)num_palette); 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_alloc_size_t)num_palette);
/* 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;
/* 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);
/* 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;
/* 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_alloc_size_t)(sizeof (png_dsort)));
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;
/* 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_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");
/* 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");
/* 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");
#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");
#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_fixed");
/* 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);
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 */
#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.
*/ staticint/* 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(>est, 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'.
*/ staticvoid/* 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;
if (png_ptr->num_trans > 0)
{ int i;
/* Ignore if all the entries are opaque (unlikely!) */ for (i=0; i<png_ptr->num_trans; ++i)
{ if (png_ptr->trans_alpha[i] == 255) continue; elseif (png_ptr->trans_alpha[i] == 0)
input_has_transparency = 1; else
{
input_has_transparency = 1;
input_has_alpha = 1; break;
}
}
}
/* If no alpha we can optimize. */ if (input_has_alpha == 0)
{ /* Any alpha means background and associative alpha processing is * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA * and ENCODE_ALPHA are irrelevant.
*/
png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
if (input_has_transparency == 0)
png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND);
}
#ifdefined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) /* png_set_background handling - deals with the complexity of whether the * background color is in the file format or the screen format in the case * where an 'expand' will happen.
*/
/* The following code cannot be entered in the alpha pre-multiplication case * because PNG_BACKGROUND_EXPAND is cancelled below.
*/ if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0 &&
(png_ptr->transformations & PNG_EXPAND) != 0)
{
{
png_ptr->background.red =
png_ptr->palette[png_ptr->background.index].red;
png_ptr->background.green =
png_ptr->palette[png_ptr->background.index].green;
png_ptr->background.blue =
png_ptr->palette[png_ptr->background.index].blue;
#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0)
{ if ((png_ptr->transformations & PNG_EXPAND_tRNS) == 0)
{ /* Invert the alpha channel (in tRNS) unless the pixels are * going to be expanded, in which case leave it for later
*/ int i, istop = png_ptr->num_trans;
for (i = 0; i < istop; i++)
png_ptr->trans_alpha[i] =
(png_byte)(255 - png_ptr->trans_alpha[i]);
}
} #endif/* READ_INVERT_ALPHA */
}
} /* background expand and (therefore) no alpha association. */ #endif/* READ_EXPAND && READ_BACKGROUND */
}
staticvoid/* PRIVATE */
png_init_rgb_transformations(png_structrp png_ptr)
{ /* Added to libpng-1.5.4: check the color type to determine whether there * is any alpha or transparency in the image and simply cancel the * background and alpha mode stuff if there isn't.
*/ int input_has_alpha = (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0; int input_has_transparency = png_ptr->num_trans > 0;
/* If no alpha we can optimize. */ if (input_has_alpha == 0)
{ /* Any alpha means background and associative alpha processing is * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA * and ENCODE_ALPHA are irrelevant.
*/ # ifdef PNG_READ_ALPHA_MODE_SUPPORTED
png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; # endif
if (input_has_transparency == 0)
png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND);
}
#ifdefined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) /* png_set_background handling - deals with the complexity of whether the * background color is in the file format or the screen format in the case * where an 'expand' will happen.
*/
/* The following code cannot be entered in the alpha pre-multiplication case * because PNG_BACKGROUND_EXPAND is cancelled below.
*/ if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0 &&
(png_ptr->transformations & PNG_EXPAND) != 0 &&
(png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) /* i.e., GRAY or GRAY_ALPHA */
{
{ /* Expand background and tRNS chunks */ int gray = png_ptr->background.gray; int trans_gray = png_ptr->trans_color.gray;
void/* PRIVATE */
png_init_read_transformations(png_structrp png_ptr)
{
png_debug(1, "in png_init_read_transformations");
/* This internal function is called from png_read_start_row in pngrutil.c * and it is called before the 'rowbytes' calculation is done, so the code * in here can change or update the transformations flags. * * First do updates that do not depend on the details of the PNG image data * being processed.
*/
#ifdef PNG_READ_GAMMA_SUPPORTED /* Prior to 1.5.4 these tests were performed from png_set_gamma, 1.5.4 adds * png_set_alpha_mode and this is another source for a default file gamma so * the test needs to be performed later - here. In addition prior to 1.5.4 * the tests were repeated for the PALETTE color type here - this is no * longer necessary (and doesn't seem to have been necessary before.)
*/
{ /* The following temporary indicates if overall gamma correction is * required.
*/ int gamma_correction = 0;
if (png_ptr->colorspace.gamma != 0) /* has been set */
{ if (png_ptr->screen_gamma != 0) /* screen set too */
gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma,
png_ptr->screen_gamma);
else /* Assume the output matches the input; a long time default behavior * of libpng, although the standard has nothing to say about this.
*/
png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma);
}
elseif (png_ptr->screen_gamma != 0) /* The converse - assume the file matches the screen, note that this * perhaps undesirable default can (from 1.5.4) be changed by calling * png_set_alpha_mode (even if the alpha handling mode isn't required * or isn't changed from the default.)
*/
png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma);
else/* neither are set */ /* Just in case the following prevents any processing - file and screen * are both assumed to be linear and there is no way to introduce a * third gamma value other than png_set_background with 'UNIQUE', and, * prior to 1.5.4
*/
png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1;
/* We have a gamma value now. */
png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
/* Now turn the gamma transformation on or off as appropriate. Notice * that PNG_GAMMA just refers to the file->screen correction. Alpha * composition may independently cause gamma correction because it needs * linear data (e.g. if the file has a gAMA chunk but the screen gamma * hasn't been specified.) In any case this flag may get turned off in * the code immediately below if the transform can be handled outside the * row loop.
*/ if (gamma_correction != 0)
png_ptr->transformations |= PNG_GAMMA;
/* Certain transformations have the effect of preventing other * transformations that happen afterward in png_do_read_transformations; * resolve the interdependencies here. From the code of * png_do_read_transformations the order is: * * 1) PNG_EXPAND (including PNG_EXPAND_tRNS) * 2) PNG_STRIP_ALPHA (if no compose) * 3) PNG_RGB_TO_GRAY * 4) PNG_GRAY_TO_RGB iff !PNG_BACKGROUND_IS_GRAY * 5) PNG_COMPOSE * 6) PNG_GAMMA * 7) PNG_STRIP_ALPHA (if compose) * 8) PNG_ENCODE_ALPHA * 9) PNG_SCALE_16_TO_8 * 10) PNG_16_TO_8 * 11) PNG_QUANTIZE (converts to palette) * 12) PNG_EXPAND_16 * 13) PNG_GRAY_TO_RGB iff PNG_BACKGROUND_IS_GRAY * 14) PNG_INVERT_MONO * 15) PNG_INVERT_ALPHA * 16) PNG_SHIFT * 17) PNG_PACK * 18) PNG_BGR * 19) PNG_PACKSWAP * 20) PNG_FILLER (includes PNG_ADD_ALPHA) * 21) PNG_SWAP_ALPHA * 22) PNG_SWAP_BYTES * 23) PNG_USER_TRANSFORM [must be last]
*/ #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 &&
(png_ptr->transformations & PNG_COMPOSE) == 0)
{ /* Stripping the alpha channel happens immediately after the 'expand' * transformations, before all other transformation, so it cancels out * the alpha handling. It has the side effect negating the effect of * PNG_EXPAND_tRNS too:
*/
png_ptr->transformations &= ~(PNG_BACKGROUND_EXPAND | PNG_ENCODE_ALPHA |
PNG_EXPAND_tRNS);
png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
/* Kill the tRNS chunk itself too. Prior to 1.5.4 this did not happen * so transparency information would remain just so long as it wasn't * expanded. This produces unexpected API changes if the set of things * that do PNG_EXPAND_tRNS changes (perfectly possible given the * documentation - which says ask for what you want, accept what you * get.) This makes the behavior consistent from 1.5.4:
*/
png_ptr->num_trans = 0;
} #endif/* STRIP_ALPHA supported, no COMPOSE */
#ifdef PNG_READ_ALPHA_MODE_SUPPORTED /* If the screen gamma is about 1.0 then the OPTIMIZE_ALPHA and ENCODE_ALPHA * settings will have no effect.
*/ if (png_gamma_significant(png_ptr->screen_gamma) == 0)
{
png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
} #endif
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED /* Make sure the coefficients for the rgb to gray conversion are set * appropriately.
*/ if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
png_colorspace_set_rgb_coefficients(png_ptr); #endif
#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED #ifdefined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) /* Detect gray background and attempt to enable optimization for * gray --> RGB case. * * Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or * RGB_ALPHA (in which case need_expand is superfluous anyway), the * background color might actually be gray yet not be flagged as such. * This is not a problem for the current code, which uses * PNG_BACKGROUND_IS_GRAY only to decide when to do the * png_do_gray_to_rgb() transformation. * * TODO: this code needs to be revised to avoid the complexity and * interdependencies. The color type of the background should be recorded in * png_set_background, along with the bit depth, then the code has a record * of exactly what color space the background is currently in.
*/ if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0)
{ /* PNG_BACKGROUND_EXPAND: the background is in the file color space, so if * the file was grayscale the background value is gray.
*/ if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
}
elseif ((png_ptr->transformations & PNG_COMPOSE) != 0)
{ /* PNG_COMPOSE: png_set_background was called with need_expand false, * so the color is in the color space of the output or png_set_alpha_mode * was called and the color is black. Ignore RGB_TO_GRAY because that * happens before GRAY_TO_RGB.
*/ if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0)
{ if (png_ptr->background.red == png_ptr->background.green &&
png_ptr->background.red == png_ptr->background.blue)
{
png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
png_ptr->background.gray = png_ptr->background.red;
}
}
} #endif/* READ_EXPAND && READ_BACKGROUND */ #endif/* READ_GRAY_TO_RGB */
/* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations * can be performed directly on the palette, and some (such as rgb to gray) * can be optimized inside the palette. This is particularly true of the * composite (background and alpha) stuff, which can be pretty much all done * in the palette even if the result is expanded to RGB or gray afterward. * * NOTE: this is Not Yet Implemented, the code behaves as in 1.5.1 and * earlier and the palette stuff is actually handled on the first row. This * leads to the reported bug that the palette returned by png_get_PLTE is not * updated.
*/ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
png_init_palette_transformations(png_ptr);
else
png_init_rgb_transformations(png_ptr);
#ifdefined(PNG_READ_BACKGROUND_SUPPORTED) && \ defined(PNG_READ_EXPAND_16_SUPPORTED) if ((png_ptr->transformations & PNG_EXPAND_16) != 0 &&
(png_ptr->transformations & PNG_COMPOSE) != 0 &&
(png_ptr->transformations & PNG_BACKGROUND_EXPAND) == 0 &&
png_ptr->bit_depth != 16)
{ /* TODO: fix this. Because the expand_16 operation is after the compose * handling the background color must be 8, not 16, bits deep, but the * application will supply a 16-bit value so reduce it here. * * The PNG_BACKGROUND_EXPAND code above does not expand to 16 bits at * present, so that case is ok (until do_expand_16 is moved.) * * NOTE: this discards the low 16 bits of the user supplied background * color, but until expand_16 works properly there is no choice!
*/ # define CHOP(x) (x)=((png_uint_16)PNG_DIV257(x))
CHOP(png_ptr->background.red);
CHOP(png_ptr->background.green);
CHOP(png_ptr->background.blue);
CHOP(png_ptr->background.gray); # undef CHOP
} #endif/* READ_BACKGROUND && READ_EXPAND_16 */
#ifdefined(PNG_READ_BACKGROUND_SUPPORTED) && \
(defined(PNG_READ_SCALE_16_TO_8_SUPPORTED) || \ defined(PNG_READ_STRIP_16_TO_8_SUPPORTED)) if ((png_ptr->transformations & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) != 0 &&
(png_ptr->transformations & PNG_COMPOSE) != 0 &&
(png_ptr->transformations & PNG_BACKGROUND_EXPAND) == 0 &&
png_ptr->bit_depth == 16)
{ /* On the other hand, if a 16-bit file is to be reduced to 8-bits per * component this will also happen after PNG_COMPOSE and so the background * color must be pre-expanded here. * * TODO: fix this too.
*/
png_ptr->background.red = (png_uint_16)(png_ptr->background.red * 257);
png_ptr->background.green =
(png_uint_16)(png_ptr->background.green * 257);
png_ptr->background.blue = (png_uint_16)(png_ptr->background.blue * 257);
png_ptr->background.gray = (png_uint_16)(png_ptr->background.gray * 257);
} #endif
/* NOTE: below 'PNG_READ_ALPHA_MODE_SUPPORTED' is presumed to also enable the * background support (see the comments in scripts/pnglibconf.dfa), this * allows pre-multiplication of the alpha channel to be implemented as * compositing on black. This is probably sub-optimal and has been done in * 1.5.4 betas simply to enable external critique and testing (i.e. to * implement the new API quickly, without lots of internal changes.)
*/
/* This needs to change - in the palette image case a whole set of tables are * built when it would be quicker to just calculate the correct value for * each palette entry directly. Also, the test is too tricky - why check * PNG_RGB_TO_GRAY if PNG_GAMMA is not set? The answer seems to be that * PNG_GAMMA is cancelled even if the gamma is known? The test excludes the * PNG_COMPOSE case, so apparently if there is no *overall* gamma correction * the gamma tables will not be built even if composition is required on a * gamma encoded value. * * In 1.5.4 this is addressed below by an additional check on the individual * file gamma - if it is not 1.0 both RGB_TO_GRAY and COMPOSE need the * tables.
*/ if ((png_ptr->transformations & PNG_GAMMA) != 0 ||
((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0 &&
(png_gamma_significant(png_ptr->colorspace.gamma) != 0 ||
png_gamma_significant(png_ptr->screen_gamma) != 0)) ||
((png_ptr->transformations & PNG_COMPOSE) != 0 &&
(png_gamma_significant(png_ptr->colorspace.gamma) != 0 ||
png_gamma_significant(png_ptr->screen_gamma) != 0 # ifdef PNG_READ_BACKGROUND_SUPPORTED
|| (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE &&
png_gamma_significant(png_ptr->background_gamma) != 0) # endif
)) || ((png_ptr->transformations & PNG_ENCODE_ALPHA) != 0 &&
png_gamma_significant(png_ptr->screen_gamma) != 0))
{
png_build_gamma_table(png_ptr, png_ptr->bit_depth);
#ifdef PNG_READ_BACKGROUND_SUPPORTED if ((png_ptr->transformations & PNG_COMPOSE) != 0)
{ /* Issue a warning about this combination: because RGB_TO_GRAY is * optimized to do the gamma transform if present yet do_background has * to do the same thing if both options are set a * double-gamma-correction happens. This is true in all versions of * libpng to date.
*/ if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
png_warning(png_ptr, "libpng does not support gamma+background+rgb_to_gray");
if ((png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) != 0)
{ /* We don't get to here unless there is a tRNS chunk with non-opaque * entries - see the checking code at the start of this function.
*/
png_color back, back_1;
png_colorp palette = png_ptr->palette; int num_palette = png_ptr->num_palette; int i; if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE)
{
switch (png_ptr->background_gamma_type)
{ case PNG_BACKGROUND_GAMMA_SCREEN:
g = (png_ptr->screen_gamma);
gs = PNG_FP_1; break;
case PNG_BACKGROUND_GAMMA_FILE:
g = png_reciprocal(png_ptr->colorspace.gamma);
gs = png_reciprocal2(png_ptr->colorspace.gamma,
png_ptr->screen_gamma); break;
case PNG_BACKGROUND_GAMMA_UNIQUE:
g = png_reciprocal(png_ptr->background_gamma);
gs = png_reciprocal2(png_ptr->background_gamma,
png_ptr->screen_gamma); break; default:
g = PNG_FP_1; /* back_1 */
gs = PNG_FP_1; /* back */ break;
}
/* Prevent the transformations being done again. * * NOTE: this is highly dubious; it removes the transformations in * place. This seems inconsistent with the general treatment of the * transformations elsewhere.
*/
png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA);
} /* color_type == PNG_COLOR_TYPE_PALETTE */
/* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ else/* color_type != PNG_COLOR_TYPE_PALETTE */
{ int gs_sig, g_sig;
png_fixed_point g = PNG_FP_1; /* Correction to linear */
png_fixed_point gs = PNG_FP_1; /* Correction to screen */
switch (png_ptr->background_gamma_type)
{ case PNG_BACKGROUND_GAMMA_SCREEN:
g = png_ptr->screen_gamma; /* gs = PNG_FP_1; */ break;
case PNG_BACKGROUND_GAMMA_FILE:
g = png_reciprocal(png_ptr->colorspace.gamma);
gs = png_reciprocal2(png_ptr->colorspace.gamma,
png_ptr->screen_gamma); break;
case PNG_BACKGROUND_GAMMA_UNIQUE:
g = png_reciprocal(png_ptr->background_gamma);
gs = png_reciprocal2(png_ptr->background_gamma,
png_ptr->screen_gamma); break;
/* The background is now in screen gamma: */
png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_SCREEN;
} /* color_type != PNG_COLOR_TYPE_PALETTE */
}/* png_ptr->transformations & PNG_BACKGROUND */
else /* Transformation does not include PNG_BACKGROUND */ #endif/* READ_BACKGROUND */ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED /* RGB_TO_GRAY needs to have non-gamma-corrected values! */
&& ((png_ptr->transformations & PNG_EXPAND) == 0 ||
(png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) #endif
)
{
png_colorp palette = png_ptr->palette; int num_palette = png_ptr->num_palette; int i;
/* NOTE: there are other transformations that should probably be in * here too.
*/ for (i = 0; i < num_palette; i++)
{
palette[i].red = png_ptr->gamma_table[palette[i].red];
palette[i].green = png_ptr->gamma_table[palette[i].green];
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.25 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.