/* pngvalid.c - validate libpng by constructing then reading png files.
*
- * Last changed in libpng 1.6.10 [March 6, 2014]
- * Copyright (c) 2014 Glenn Randers-Pehrson
- * Written by John Cunningham Bowler
+ * Copyright (c) 2021 Cosmin Truta
+ * Copyright (c) 2014-2017 John Cunningham Bowler
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
#define _POSIX_SOURCE 1
#define _ISOC99_SOURCE 1 /* For floating point */
#define _GNU_SOURCE 1 /* For the floating point exception extension */
+#define _BSD_SOURCE 1 /* For the floating point exception extension */
+#define _DEFAULT_SOURCE 1 /* For the floating point exception extension */
#include <signal.h>
#include <stdio.h>
# include <fenv.h>
#endif
+#ifndef FE_DIVBYZERO
+# define FE_DIVBYZERO 0
+#endif
+#ifndef FE_INVALID
+# define FE_INVALID 0
+#endif
+#ifndef FE_OVERFLOW
+# define FE_OVERFLOW 0
+#endif
+
/* Define the following to use this test against your installed libpng, rather
* than the one being built here:
*/
/* 1.6.1 added support for the configure test harness, which uses 77 to indicate
* a skipped test, in earlier versions we need to succeed on a skipped test, so:
*/
-#if PNG_LIBPNG_VER < 10601
-# define SKIP 0
-#else
+#if PNG_LIBPNG_VER >= 10601 && defined(HAVE_CONFIG_H)
# define SKIP 77
+#else
+# define SKIP 0
#endif
/* pngvalid requires write support and one of the fixed or floating point APIs.
(defined(PNG_FIXED_POINT_SUPPORTED) || defined(PNG_FLOATING_POINT_SUPPORTED))
#if PNG_LIBPNG_VER < 10500
-/* This deliberately lacks the PNG_CONST. */
+/* This deliberately lacks the const. */
typedef png_byte *png_const_bytep;
/* This is copied from 1.5.1 png.h: */
#define PNG_WRITE_16BIT_SUPPORTED
#define PNG_READ_16BIT_SUPPORTED
-/* This comes from pnglibconf.h afer 1.5: */
+/* This comes from pnglibconf.h after 1.5: */
#define PNG_FP_1 100000
#define PNG_GAMMA_THRESHOLD_FIXED\
((png_fixed_point)(PNG_GAMMA_THRESHOLD * PNG_FP_1))
# define png_const_structp png_structp
#endif
+#ifndef RELEASE_BUILD
+ /* RELEASE_BUILD is true for releases and release candidates: */
+# define RELEASE_BUILD (PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC)
+#endif
+#if RELEASE_BUILD
+# define debugonly(something)
+#else /* !RELEASE_BUILD */
+# define debugonly(something) something
+#endif /* !RELEASE_BUILD */
+
#include <float.h> /* For floating point constants */
#include <stdlib.h> /* For malloc */
#include <string.h> /* For memcpy, memset */
#include <math.h> /* For floor */
+/* Convenience macros. */
+#define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
+#define CHUNK_IHDR CHUNK(73,72,68,82)
+#define CHUNK_PLTE CHUNK(80,76,84,69)
+#define CHUNK_IDAT CHUNK(73,68,65,84)
+#define CHUNK_IEND CHUNK(73,69,78,68)
+#define CHUNK_cHRM CHUNK(99,72,82,77)
+#define CHUNK_gAMA CHUNK(103,65,77,65)
+#define CHUNK_sBIT CHUNK(115,66,73,84)
+#define CHUNK_sRGB CHUNK(115,82,71,66)
+
/* Unused formal parameter errors are removed using the following macro which is
* expected to have no bad effects on performance.
*/
&(ps)->exception_context
#define context(ps,fault) anon_context(ps); png_store *fault
+/* This macro returns the number of elements in an array as an (unsigned int),
+ * it is necessary to avoid the inability of certain versions of GCC to use
+ * the value of a compile-time constant when performing range checks. It must
+ * be passed an array name.
+ */
+#define ARRAY_SIZE(a) ((unsigned int)((sizeof (a))/(sizeof (a)[0])))
+
+/* GCC BUG 66447 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66447) requires
+ * some broken GCC versions to be fixed up to avoid invalid whining about auto
+ * variables that are *not* changed within the scope of a setjmp being changed.
+ *
+ * Feel free to extend the list of broken versions.
+ */
+#define is_gnu(major,minor)\
+ (defined __GNUC__) && __GNUC__ == (major) && __GNUC_MINOR__ == (minor)
+#define is_gnu_patch(major,minor,patch)\
+ is_gnu(major,minor) && __GNUC_PATCHLEVEL__ == 0
+/* For the moment just do it always; all versions of GCC seem to be broken: */
+#ifdef __GNUC__
+ const void * volatile make_volatile_for_gnu;
+# define gnu_volatile(x) make_volatile_for_gnu = &x;
+#else /* !GNUC broken versions */
+# define gnu_volatile(x)
+#endif /* !GNUC broken versions */
+
/******************************* UTILITIES ************************************/
/* Error handling is particularly problematic in production code - error
* handlers often themselves have bugs which lead to programs that detect
* warning messages into buffers that are too small.
*/
static size_t safecat(char *buffer, size_t bufsize, size_t pos,
- PNG_CONST char *cat)
+ const char *cat)
{
while (pos < bufsize && cat != NULL && *cat != 0)
buffer[pos++] = *cat++;
}
#endif
-static PNG_CONST char invalid[] = "invalid";
-static PNG_CONST char sep[] = ": ";
+static const char invalid[] = "invalid";
+static const char sep[] = ": ";
-static PNG_CONST char *colour_types[8] =
+static const char *colour_types[8] =
{
"grayscale", invalid, "truecolour", "indexed-colour",
"grayscale with alpha", invalid, "truecolour with alpha", invalid
};
-#ifdef PNG_READ_SUPPORTED
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
/* Convert a double precision value to fixed point. */
static png_fixed_point
fix(double d)
make_random_bytes(seed, bytes, 4);
}
-#ifdef PNG_READ_SUPPORTED
+#if defined PNG_READ_SUPPORTED || defined PNG_WRITE_tRNS_SUPPORTED ||\
+ defined PNG_WRITE_FILTER_SUPPORTED
static void
randomize(void *pv, size_t size)
{
make_random_bytes(random_seed, pv, size);
}
-#define RANDOMIZE(this) randomize(&(this), sizeof (this))
+#define R8(this) randomize(&(this), sizeof (this))
-static unsigned int
-random_mod(unsigned int max)
+#ifdef PNG_READ_SUPPORTED
+static png_byte
+random_byte(void)
{
- unsigned int x;
+ unsigned char b1[1];
+ randomize(b1, sizeof b1);
+ return b1[0];
+}
+#endif /* READ */
- RANDOMIZE(x);
+static png_uint_16
+random_u16(void)
+{
+ unsigned char b2[2];
+ randomize(b2, sizeof b2);
+ return png_get_uint_16(b2);
+}
- return x % max; /* 0 .. max-1 */
+#if defined PNG_READ_RGB_TO_GRAY_SUPPORTED ||\
+ defined PNG_READ_FILLER_SUPPORTED
+static png_uint_32
+random_u32(void)
+{
+ unsigned char b4[4];
+ randomize(b4, sizeof b4);
+ return png_get_uint_32(b4);
}
+#endif /* READ_FILLER || READ_RGB_TO_GRAY */
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+#endif /* READ || WRITE_tRNS || WRITE_FILTER */
+
+#if defined PNG_READ_TRANSFORMS_SUPPORTED ||\
+ defined PNG_WRITE_FILTER_SUPPORTED
+static unsigned int
+random_mod(unsigned int max)
+{
+ return random_u16() % max; /* 0 .. max-1 */
+}
+#endif /* READ_TRANSFORMS || WRITE_FILTER */
+
+#if (defined PNG_READ_RGB_TO_GRAY_SUPPORTED) ||\
+ (defined PNG_READ_FILLER_SUPPORTED)
static int
random_choice(void)
{
- unsigned char x;
-
- RANDOMIZE(x);
-
- return x & 1;
+ return random_byte() & 1;
}
-#endif
-#endif /* PNG_READ_SUPPORTED */
+#endif /* READ_RGB_TO_GRAY || READ_FILLER */
/* A numeric ID based on PNG file characteristics. The 'do_interlace' field
* simply records whether pngvalid did the interlace itself or whether it
* was done by libpng. Width and height must be less than 256. 'palette' is an
- * index of the palette to use for formats with a palette (0 otherwise.)
+ * index of the palette to use for formats with a palette otherwise a boolean
+ * indicating if a tRNS chunk was generated.
*/
#define FILEID(col, depth, palette, interlace, width, height, do_interlace) \
((png_uint_32)((col) + ((depth)<<3) + ((palette)<<8) + ((interlace)<<13) + \
#define COL_FROM_ID(id) ((png_byte)((id)& 0x7U))
#define DEPTH_FROM_ID(id) ((png_byte)(((id) >> 3) & 0x1fU))
#define PALETTE_FROM_ID(id) (((id) >> 8) & 0x1f)
-#define INTERLACE_FROM_ID(id) ((int)(((id) >> 13) & 0x3))
+#define INTERLACE_FROM_ID(id) ((png_byte)(((id) >> 13) & 0x3))
#define DO_INTERLACE_FROM_ID(id) ((int)(((id)>>15) & 1))
#define WIDTH_FROM_ID(id) (((id)>>16) & 0xff)
#define HEIGHT_FROM_ID(id) (((id)>>24) & 0xff)
png_uint_32 w, png_uint_32 h, int do_interlace)
{
pos = safecat(buffer, bufsize, pos, colour_types[colour_type]);
- if (npalette > 0)
+ if (colour_type == 3) /* must have a palette */
{
pos = safecat(buffer, bufsize, pos, "[");
pos = safecatn(buffer, bufsize, pos, npalette);
pos = safecat(buffer, bufsize, pos, "]");
}
+
+ else if (npalette != 0)
+ pos = safecat(buffer, bufsize, pos, "+tRNS");
+
pos = safecat(buffer, bufsize, pos, " ");
pos = safecatn(buffer, bufsize, pos, bit_depth);
pos = safecat(buffer, bufsize, pos, " bit");
static int
next_format(png_bytep colour_type, png_bytep bit_depth,
- unsigned int* palette_number, int no_low_depth_gray)
+ unsigned int* palette_number, int low_depth_gray, int tRNS)
{
if (*bit_depth == 0)
{
*colour_type = 0;
- if (no_low_depth_gray)
- *bit_depth = 8;
- else
+ if (low_depth_gray)
*bit_depth = 1;
+ else
+ *bit_depth = 8;
*palette_number = 0;
return 1;
}
- if (*colour_type == 3)
+ if (*colour_type < 4/*no alpha channel*/)
{
- /* Add multiple palettes for colour type 3. */
- if (++*palette_number < PALETTE_COUNT(*bit_depth))
+ /* Add multiple palettes for colour type 3, one image with tRNS
+ * and one without for other non-alpha formats:
+ */
+ unsigned int pn = ++*palette_number;
+ png_byte ct = *colour_type;
+
+ if (((ct == 0/*GRAY*/ || ct/*RGB*/ == 2) && tRNS && pn < 2) ||
+ (ct == 3/*PALETTE*/ && pn < PALETTE_COUNT(*bit_depth)))
return 1;
+ /* No: next bit depth */
*palette_number = 0;
}
#ifdef PNG_READ_TRANSFORMS_SUPPORTED
static unsigned int
sample(png_const_bytep row, png_byte colour_type, png_byte bit_depth,
- png_uint_32 x, unsigned int sample_index)
+ png_uint_32 x, unsigned int sample_index, int swap16, int littleendian)
{
png_uint_32 bit_index, result;
return result;
else if (bit_depth > 8)
- return (result << 8) + *++row;
+ {
+ if (swap16)
+ return (*++row << 8) + result;
+ else
+ return (result << 8) + *++row;
+ }
- /* Less than 8 bits per sample. */
+ /* Less than 8 bits per sample. By default PNG has the big end of
+ * the egg on the left of the screen, but if littleendian is set
+ * then the big end is on the right.
+ */
bit_index &= 7;
- return (result >> (8-bit_index-bit_depth)) & ((1U<<bit_depth)-1);
+
+ if (!littleendian)
+ bit_index = 8-bit_index-bit_depth;
+
+ return (result >> bit_index) & ((1U<<bit_depth)-1);
}
#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
*/
static void
pixel_copy(png_bytep toBuffer, png_uint_32 toIndex,
- png_const_bytep fromBuffer, png_uint_32 fromIndex, unsigned int pixelSize)
+ png_const_bytep fromBuffer, png_uint_32 fromIndex, unsigned int pixelSize,
+ int littleendian)
{
/* Assume we can multiply by 'size' without overflow because we are
* just working in a single buffer.
if (pixelSize < 8) /* Sub-byte */
{
/* Mask to select the location of the copied pixel: */
- unsigned int destMask = ((1U<<pixelSize)-1) << (8-pixelSize-(toIndex&7));
+ unsigned int destMask = ((1U<<pixelSize)-1) <<
+ (littleendian ? toIndex&7 : 8-pixelSize-(toIndex&7));
/* The following read the entire pixels and clears the extra: */
unsigned int destByte = toBuffer[toIndex >> 3] & ~destMask;
unsigned int sourceByte = fromBuffer[fromIndex >> 3];
/* Don't rely on << or >> supporting '0' here, just in case: */
fromIndex &= 7;
- if (fromIndex > 0) sourceByte <<= fromIndex;
- if ((toIndex & 7) > 0) sourceByte >>= toIndex & 7;
+ if (littleendian)
+ {
+ if (fromIndex > 0) sourceByte >>= fromIndex;
+ if ((toIndex & 7) > 0) sourceByte <<= toIndex & 7;
+ }
+
+ else
+ {
+ if (fromIndex > 0) sourceByte <<= fromIndex;
+ if ((toIndex & 7) > 0) sourceByte >>= toIndex & 7;
+ }
toBuffer[toIndex >> 3] = (png_byte)(destByte | (sourceByte & destMask));
}
* bytes at the end.
*/
static void
-row_copy(png_bytep toBuffer, png_const_bytep fromBuffer, unsigned int bitWidth)
+row_copy(png_bytep toBuffer, png_const_bytep fromBuffer, unsigned int bitWidth,
+ int littleendian)
{
memcpy(toBuffer, fromBuffer, bitWidth >> 3);
toBuffer += bitWidth >> 3;
fromBuffer += bitWidth >> 3;
- /* The remaining bits are in the top of the byte, the mask is the bits to
- * retain.
- */
- mask = 0xff >> (bitWidth & 7);
+ if (littleendian)
+ mask = 0xff << (bitWidth & 7);
+ else
+ mask = 0xff >> (bitWidth & 7);
*toBuffer = (png_byte)((*toBuffer & mask) | (*fromBuffer & ~mask));
}
}
{
struct png_store_file* next; /* as many as you like... */
char name[FILE_NAME_SIZE];
+ unsigned int IDAT_bits; /* Number of bits in IDAT size */
+ png_uint_32 IDAT_size; /* Total size of IDAT data */
png_uint_32 id; /* must be correct (see FILEID) */
- png_size_t datacount; /* In this (the last) buffer */
+ size_t datacount; /* In this (the last) buffer */
png_store_buffer data; /* Last buffer in file */
int npalette; /* Number of entries in palette */
store_palette_entry* palette; /* May be NULL */
char test[128]; /* Name of test */
char error[256];
+ /* Share fields */
+ png_uint_32 chunklen; /* Length of chunk+overhead (chunkpos >= 8) */
+ png_uint_32 chunktype;/* Type of chunk (valid if chunkpos >= 4) */
+ png_uint_32 chunkpos; /* Position in chunk */
+ png_uint_32 IDAT_size;/* Accumulated IDAT size in .new */
+ unsigned int IDAT_bits;/* Cache of the file store value */
+
/* Read fields */
png_structp pread; /* Used to read a saved file */
png_infop piread;
png_store_file* current; /* Set when reading */
png_store_buffer* next; /* Set when reading */
- png_size_t readpos; /* Position in *next */
+ size_t readpos; /* Position in *next */
png_byte* image; /* Buffer for reading interlaced images */
- png_size_t cb_image; /* Size of this buffer */
- png_size_t cb_row; /* Row size of the image(s) */
+ size_t cb_image; /* Size of this buffer */
+ size_t cb_row; /* Row size of the image(s) */
+ uLong IDAT_crc;
+ png_uint_32 IDAT_len; /* Used when re-chunking IDAT chunks */
+ png_uint_32 IDAT_pos; /* Used when re-chunking IDAT chunks */
png_uint_32 image_h; /* Number of rows in a single image */
store_pool read_memory_pool;
png_store_file* saved;
png_structp pwrite; /* Used when writing a new file */
png_infop piwrite;
- png_size_t writepos; /* Position in .new */
+ size_t writepos; /* Position in .new */
char wname[FILE_NAME_SIZE];
png_store_buffer new; /* The end of the new PNG file being written. */
store_pool write_memory_pool;
make_four_random_bytes(store_seed, mark);
}
-#ifdef PNG_READ_SUPPORTED
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
/* Use this for random 32 bit values; this function makes sure the result is
* non-zero.
*/
random_32(void)
{
- for(;;)
+ for (;;)
{
png_byte mark[4];
png_uint_32 result;
ps->pwrite = NULL;
ps->piwrite = NULL;
ps->writepos = 0;
+ ps->chunkpos = 8;
+ ps->chunktype = 0;
+ ps->chunklen = 16;
+ ps->IDAT_size = 0;
+ ps->IDAT_bits = 0;
ps->new.prev = NULL;
ps->palette = NULL;
ps->npalette = 0;
{
store_freebuffer(&ps->new);
ps->writepos = 0;
+ ps->chunkpos = 8;
+ ps->chunktype = 0;
+ ps->chunklen = 16;
+ ps->IDAT_size = 0;
+ ps->IDAT_bits = 0;
if (ps->palette != NULL)
{
free(ps->palette);
{
png_store_buffer *pb;
- if (ps->writepos != STORE_BUFFER_SIZE)
- png_error(ps->pwrite, "invalid store call");
-
pb = voidcast(png_store_buffer*, malloc(sizeof *pb));
if (pb == NULL)
}
}
-/* Main interface to file storeage, after writing a new PNG file (see the API
+static unsigned int
+bits_of(png_uint_32 num)
+{
+ /* Return the number of bits in 'num' */
+ unsigned int b = 0;
+
+ if (num & 0xffff0000U) b += 16U, num >>= 16;
+ if (num & 0xff00U) b += 8U, num >>= 8;
+ if (num & 0xf0U) b += 4U, num >>= 4;
+ if (num & 0xcU) b += 2U, num >>= 2;
+ if (num & 0x2U) ++b, num >>= 1;
+ if (num) ++b;
+
+ return b; /* 0..32 */
+}
+
+/* Main interface to file storage, after writing a new PNG file (see the API
* below) call store_storefile to store the result with the given name and id.
*/
static void
store_storefile(png_store *ps, png_uint_32 id)
{
- png_store_file *pf = voidcast(png_store_file*, malloc(sizeof *pf));
+ png_store_file *pf;
+
+ if (ps->chunkpos != 0U || ps->chunktype != 0U || ps->chunklen != 0U ||
+ ps->IDAT_size == 0)
+ png_error(ps->pwrite, "storefile: incomplete write");
+
+ pf = voidcast(png_store_file*, malloc(sizeof *pf));
if (pf == NULL)
png_error(ps->pwrite, "storefile: OOM");
safecat(pf->name, sizeof pf->name, 0, ps->wname);
pf->id = id;
pf->data = ps->new;
pf->datacount = ps->writepos;
+ pf->IDAT_size = ps->IDAT_size;
+ pf->IDAT_bits = bits_of(ps->IDAT_size);
+ /* Because the IDAT always has zlib header stuff this must be true: */
+ if (pf->IDAT_bits == 0U)
+ png_error(ps->pwrite, "storefile: 0 sized IDAT");
ps->new.prev = NULL;
ps->writepos = 0;
+ ps->chunkpos = 8;
+ ps->chunktype = 0;
+ ps->chunklen = 16;
+ ps->IDAT_size = 0;
pf->palette = ps->palette;
pf->npalette = ps->npalette;
ps->palette = 0;
/* Generate an error message (in the given buffer) */
static size_t
store_message(png_store *ps, png_const_structp pp, char *buffer, size_t bufsize,
- size_t pos, PNG_CONST char *msg)
+ size_t pos, const char *msg)
{
if (pp != NULL && pp == ps->pread)
{
*/
/* Return a single row from the correct image. */
static png_bytep
-store_image_row(PNG_CONST png_store* ps, png_const_structp pp, int nImage,
+store_image_row(const png_store* ps, png_const_structp pp, int nImage,
png_uint_32 y)
{
- png_size_t coffset = (nImage * ps->image_h + y) * (ps->cb_row + 5) + 2;
+ size_t coffset = (nImage * ps->image_h + y) * (ps->cb_row + 5) + 2;
if (ps->image == NULL)
png_error(pp, "no allocated image");
static void
store_ensure_image(png_store *ps, png_const_structp pp, int nImages,
- png_size_t cbRow, png_uint_32 cRows)
+ size_t cbRow, png_uint_32 cRows)
{
- png_size_t cb = nImages * cRows * (cbRow + 5);
+ size_t cb = nImages * cRows * (cbRow + 5);
if (ps->cb_image < cb)
{
#ifdef PNG_READ_SUPPORTED
static void
-store_image_check(PNG_CONST png_store* ps, png_const_structp pp, int iImage)
+store_image_check(const png_store* ps, png_const_structp pp, int iImage)
{
png_const_bytep image = ps->image;
png_error(pp, "image overwrite");
else
{
- png_size_t cbRow = ps->cb_row;
+ size_t cbRow = ps->cb_row;
png_uint_32 rows = ps->image_h;
image += iImage * (cbRow+5) * ps->image_h;
image += 2; /* skip image first row markers */
- while (rows-- > 0)
+ for (; rows > 0; --rows)
{
if (image[-2] != 190 || image[-1] != 239)
png_error(pp, "row start overwritten");
}
#endif /* PNG_READ_SUPPORTED */
+static int
+valid_chunktype(png_uint_32 chunktype)
+{
+ /* Each byte in the chunk type must be in one of the ranges 65..90, 97..122
+ * (both inclusive), so:
+ */
+ unsigned int i;
+
+ for (i=0; i<4; ++i)
+ {
+ unsigned int c = chunktype & 0xffU;
+
+ if (!((c >= 65U && c <= 90U) || (c >= 97U && c <= 122U)))
+ return 0;
+
+ chunktype >>= 8;
+ }
+
+ return 1; /* It's valid */
+}
+
static void PNGCBAPI
-store_write(png_structp ppIn, png_bytep pb, png_size_t st)
+store_write(png_structp ppIn, png_bytep pb, size_t st)
{
png_const_structp pp = ppIn;
png_store *ps = voidcast(png_store*, png_get_io_ptr(pp));
+ size_t writepos = ps->writepos;
+ png_uint_32 chunkpos = ps->chunkpos;
+ png_uint_32 chunktype = ps->chunktype;
+ png_uint_32 chunklen = ps->chunklen;
if (ps->pwrite != pp)
png_error(pp, "store state damaged");
+ /* Technically this is legal, but in practice libpng never writes more than
+ * the maximum chunk size at once so if it happens something weird has
+ * changed inside libpng (probably).
+ */
+ if (st > 0x7fffffffU)
+ png_error(pp, "unexpected write size");
+
+ /* Now process the bytes to be written. Do this in units of the space in the
+ * output (write) buffer or, at the start 4 bytes for the chunk type and
+ * length limited in any case by the amount of data.
+ */
while (st > 0)
{
- size_t cb;
+ if (writepos >= STORE_BUFFER_SIZE)
+ store_storenew(ps), writepos = 0;
+
+ if (chunkpos < 4)
+ {
+ png_byte b = *pb++;
+ --st;
+ chunklen = (chunklen << 8) + b;
+ ps->new.buffer[writepos++] = b;
+ ++chunkpos;
+ }
+
+ else if (chunkpos < 8)
+ {
+ png_byte b = *pb++;
+ --st;
+ chunktype = (chunktype << 8) + b;
+ ps->new.buffer[writepos++] = b;
+
+ if (++chunkpos == 8)
+ {
+ chunklen &= 0xffffffffU;
+ if (chunklen > 0x7fffffffU)
+ png_error(pp, "chunk length too great");
- if (ps->writepos >= STORE_BUFFER_SIZE)
- store_storenew(ps);
+ chunktype &= 0xffffffffU;
+ if (chunktype == CHUNK_IDAT)
+ {
+ if (chunklen > ~ps->IDAT_size)
+ png_error(pp, "pngvalid internal image too large");
- cb = st;
+ ps->IDAT_size += chunklen;
+ }
- if (cb > STORE_BUFFER_SIZE - ps->writepos)
- cb = STORE_BUFFER_SIZE - ps->writepos;
+ else if (!valid_chunktype(chunktype))
+ png_error(pp, "invalid chunk type");
- memcpy(ps->new.buffer + ps->writepos, pb, cb);
- pb += cb;
- st -= cb;
- ps->writepos += cb;
- }
+ chunklen += 12; /* for header and CRC */
+ }
+ }
+
+ else /* chunkpos >= 8 */
+ {
+ size_t cb = st;
+
+ if (cb > STORE_BUFFER_SIZE - writepos)
+ cb = STORE_BUFFER_SIZE - writepos;
+
+ if (cb > chunklen - chunkpos/* bytes left in chunk*/)
+ cb = (size_t)/*SAFE*/(chunklen - chunkpos);
+
+ memcpy(ps->new.buffer + writepos, pb, cb);
+ chunkpos += (png_uint_32)/*SAFE*/cb;
+ pb += cb;
+ writepos += cb;
+ st -= cb;
+
+ if (chunkpos >= chunklen) /* must be equal */
+ chunkpos = chunktype = chunklen = 0;
+ }
+ } /* while (st > 0) */
+
+ ps->writepos = writepos;
+ ps->chunkpos = chunkpos;
+ ps->chunktype = chunktype;
+ ps->chunklen = chunklen;
}
static void PNGCBAPI
return ps->current->datacount;
}
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
/* Return total bytes available for read. */
static size_t
store_read_buffer_avail(png_store *ps)
return 0;
}
-#endif
static int
store_read_buffer_next(png_store *ps)
* during progressive read, where the io_ptr is set internally by libpng.
*/
static void
-store_read_imp(png_store *ps, png_bytep pb, png_size_t st)
+store_read_imp(png_store *ps, png_bytep pb, size_t st)
{
if (ps->current == NULL || ps->next == NULL)
png_error(ps->pread, "store state damaged");
}
}
+static size_t
+store_read_chunk(png_store *ps, png_bytep pb, size_t max, size_t min)
+{
+ png_uint_32 chunklen = ps->chunklen;
+ png_uint_32 chunktype = ps->chunktype;
+ png_uint_32 chunkpos = ps->chunkpos;
+ size_t st = max;
+
+ if (st > 0) do
+ {
+ if (chunkpos >= chunklen) /* end of last chunk */
+ {
+ png_byte buffer[8];
+
+ /* Read the header of the next chunk: */
+ store_read_imp(ps, buffer, 8U);
+ chunklen = png_get_uint_32(buffer) + 12U;
+ chunktype = png_get_uint_32(buffer+4U);
+ chunkpos = 0U; /* Position read so far */
+ }
+
+ if (chunktype == CHUNK_IDAT)
+ {
+ png_uint_32 IDAT_pos = ps->IDAT_pos;
+ png_uint_32 IDAT_len = ps->IDAT_len;
+ png_uint_32 IDAT_size = ps->IDAT_size;
+
+ /* The IDAT headers are constructed here; skip the input header. */
+ if (chunkpos < 8U)
+ chunkpos = 8U;
+
+ if (IDAT_pos == IDAT_len)
+ {
+ png_byte random = random_byte();
+
+ /* Make a new IDAT chunk, if IDAT_len is 0 this is the first IDAT,
+ * if IDAT_size is 0 this is the end. At present this is set up
+ * using a random number so that there is a 25% chance before
+ * the start of the first IDAT chunk being 0 length.
+ */
+ if (IDAT_len == 0U) /* First IDAT */
+ {
+ switch (random & 3U)
+ {
+ case 0U: IDAT_len = 12U; break; /* 0 bytes */
+ case 1U: IDAT_len = 13U; break; /* 1 byte */
+ default: IDAT_len = random_u32();
+ IDAT_len %= IDAT_size;
+ IDAT_len += 13U; /* 1..IDAT_size bytes */
+ break;
+ }
+ }
+
+ else if (IDAT_size == 0U) /* all IDAT data read */
+ {
+ /* The last (IDAT) chunk should be positioned at the CRC now: */
+ if (chunkpos != chunklen-4U)
+ png_error(ps->pread, "internal: IDAT size mismatch");
+
+ /* The only option here is to add a zero length IDAT, this
+ * happens 25% of the time. Because of the check above
+ * chunklen-4U-chunkpos must be zero, we just need to skip the
+ * CRC now.
+ */
+ if ((random & 3U) == 0U)
+ IDAT_len = 12U; /* Output another 0 length IDAT */
+
+ else
+ {
+ /* End of IDATs, skip the CRC to make the code above load the
+ * next chunk header next time round.
+ */
+ png_byte buffer[4];
+
+ store_read_imp(ps, buffer, 4U);
+ chunkpos += 4U;
+ ps->IDAT_pos = IDAT_pos;
+ ps->IDAT_len = IDAT_len;
+ ps->IDAT_size = 0U;
+ continue; /* Read the next chunk */
+ }
+ }
+
+ else
+ {
+ /* Middle of IDATs, use 'random' to determine the number of bits
+ * to use in the IDAT length.
+ */
+ IDAT_len = random_u32();
+ IDAT_len &= (1U << (1U + random % ps->IDAT_bits)) - 1U;
+ if (IDAT_len > IDAT_size)
+ IDAT_len = IDAT_size;
+ IDAT_len += 12U; /* zero bytes may occur */
+ }
+
+ IDAT_pos = 0U;
+ ps->IDAT_crc = 0x35af061e; /* Ie: crc32(0UL, "IDAT", 4) */
+ } /* IDAT_pos == IDAT_len */
+
+ if (IDAT_pos < 8U) /* Return the header */ do
+ {
+ png_uint_32 b;
+ unsigned int shift;
+
+ if (IDAT_pos < 4U)
+ b = IDAT_len - 12U;
+
+ else
+ b = CHUNK_IDAT;
+
+ shift = 3U & IDAT_pos;
+ ++IDAT_pos;
+
+ if (shift < 3U)
+ b >>= 8U*(3U-shift);
+
+ *pb++ = 0xffU & b;
+ }
+ while (--st > 0 && IDAT_pos < 8);
+
+ else if (IDAT_pos < IDAT_len - 4U) /* I.e not the CRC */
+ {
+ if (chunkpos < chunklen-4U)
+ {
+ uInt avail = (uInt)-1;
+
+ if (avail > (IDAT_len-4U) - IDAT_pos)
+ avail = (uInt)/*SAFE*/((IDAT_len-4U) - IDAT_pos);
+
+ if (avail > st)
+ avail = (uInt)/*SAFE*/st;
+
+ if (avail > (chunklen-4U) - chunkpos)
+ avail = (uInt)/*SAFE*/((chunklen-4U) - chunkpos);
+
+ store_read_imp(ps, pb, avail);
+ ps->IDAT_crc = crc32(ps->IDAT_crc, pb, avail);
+ pb += (size_t)/*SAFE*/avail;
+ st -= (size_t)/*SAFE*/avail;
+ chunkpos += (png_uint_32)/*SAFE*/avail;
+ IDAT_size -= (png_uint_32)/*SAFE*/avail;
+ IDAT_pos += (png_uint_32)/*SAFE*/avail;
+ }
+
+ else /* skip the input CRC */
+ {
+ png_byte buffer[4];
+
+ store_read_imp(ps, buffer, 4U);
+ chunkpos += 4U;
+ }
+ }
+
+ else /* IDAT crc */ do
+ {
+ uLong b = ps->IDAT_crc;
+ unsigned int shift = (IDAT_len - IDAT_pos); /* 4..1 */
+ ++IDAT_pos;
+
+ if (shift > 1U)
+ b >>= 8U*(shift-1U);
+
+ *pb++ = 0xffU & b;
+ }
+ while (--st > 0 && IDAT_pos < IDAT_len);
+
+ ps->IDAT_pos = IDAT_pos;
+ ps->IDAT_len = IDAT_len;
+ ps->IDAT_size = IDAT_size;
+ }
+
+ else /* !IDAT */
+ {
+ /* If there is still some pending IDAT data after the IDAT chunks have
+ * been processed there is a problem:
+ */
+ if (ps->IDAT_len > 0 && ps->IDAT_size > 0)
+ png_error(ps->pread, "internal: missing IDAT data");
+
+ if (chunktype == CHUNK_IEND && ps->IDAT_len == 0U)
+ png_error(ps->pread, "internal: missing IDAT");
+
+ if (chunkpos < 8U) /* Return the header */ do
+ {
+ png_uint_32 b;
+ unsigned int shift;
+
+ if (chunkpos < 4U)
+ b = chunklen - 12U;
+
+ else
+ b = chunktype;
+
+ shift = 3U & chunkpos;
+ ++chunkpos;
+
+ if (shift < 3U)
+ b >>= 8U*(3U-shift);
+
+ *pb++ = 0xffU & b;
+ }
+ while (--st > 0 && chunkpos < 8);
+
+ else /* Return chunk bytes, including the CRC */
+ {
+ size_t avail = st;
+
+ if (avail > chunklen - chunkpos)
+ avail = (size_t)/*SAFE*/(chunklen - chunkpos);
+
+ store_read_imp(ps, pb, avail);
+ pb += avail;
+ st -= avail;
+ chunkpos += (png_uint_32)/*SAFE*/avail;
+
+ /* Check for end of chunk and end-of-file; don't try to read a new
+ * chunk header at this point unless instructed to do so by 'min'.
+ */
+ if (chunkpos >= chunklen && max-st >= min &&
+ store_read_buffer_avail(ps) == 0)
+ break;
+ }
+ } /* !IDAT */
+ }
+ while (st > 0);
+
+ ps->chunklen = chunklen;
+ ps->chunktype = chunktype;
+ ps->chunkpos = chunkpos;
+
+ return st; /* space left */
+}
+
static void PNGCBAPI
-store_read(png_structp ppIn, png_bytep pb, png_size_t st)
+store_read(png_structp ppIn, png_bytep pb, size_t st)
{
png_const_structp pp = ppIn;
png_store *ps = voidcast(png_store*, png_get_io_ptr(pp));
if (ps == NULL || ps->pread != pp)
png_error(pp, "bad store read call");
- store_read_imp(ps, pb, st);
+ store_read_chunk(ps, pb, st, st);
}
static void
store_progressive_read(png_store *ps, png_structp pp, png_infop pi)
{
- /* Notice that a call to store_read will cause this function to fail because
- * readpos will be set.
- */
if (ps->pread != pp || ps->current == NULL || ps->next == NULL)
png_error(pp, "store state damaged (progressive)");
- do
+ /* This is another Horowitz and Hill random noise generator. In this case
+ * the aim is to stress the progressive reader with truly horrible variable
+ * buffer sizes in the range 1..500, so a sequence of 9 bit random numbers
+ * is generated. We could probably just count from 1 to 32767 and get as
+ * good a result.
+ */
+ while (store_read_buffer_avail(ps) > 0)
{
- if (ps->readpos != 0)
- png_error(pp, "store_read called during progressive read");
+ static png_uint_32 noise = 2;
+ size_t cb;
+ png_byte buffer[512];
- png_process_data(pp, pi, ps->next->buffer, store_read_buffer_size(ps));
+ /* Generate 15 more bits of stuff: */
+ noise = (noise << 9) | ((noise ^ (noise >> (9-5))) & 0x1ff);
+ cb = noise & 0x1ff;
+ cb -= store_read_chunk(ps, buffer, cb, 1);
+ png_process_data(pp, pi, buffer, cb);
}
- while (store_read_buffer_next(ps));
}
#endif /* PNG_READ_SUPPORTED */
* operation.)
*/
if (ps->current == NULL)
+ {
store_log(ps, ps->pread, "no current stream for palette", 1);
+ return NULL;
+ }
/* The result may be null if there is no palette. */
*npalette = ps->current->npalette;
* all the memory.
*/
static void
-store_pool_error(png_store *ps, png_const_structp pp, PNG_CONST char *msg)
+store_pool_error(png_store *ps, png_const_structp pp, const char *msg)
{
if (pp != NULL)
png_error(pp, msg);
next->next = NULL;
fprintf(stderr, "\t%lu bytes @ %p\n",
- (unsigned long)next->size, (PNG_CONST void*)(next+1));
+ (unsigned long)next->size, (const void*)(next+1));
/* The NULL means this will always return, even if the memory is
* corrupted.
*/
* returned libpng structures as destroyed by store_write_reset above.
*/
static png_structp
-set_store_for_write(png_store *ps, png_infopp ppi,
- PNG_CONST char * volatile name)
+set_store_for_write(png_store *ps, png_infopp ppi, const char *name)
{
anon_context(ps);
ps->next = NULL;
ps->readpos = 0;
ps->validated = 0;
+
+ ps->chunkpos = 8;
+ ps->chunktype = 0;
+ ps->chunklen = 16;
+ ps->IDAT_size = 0;
}
#ifdef PNG_READ_SUPPORTED
{
ps->current = pf;
ps->next = NULL;
+ ps->IDAT_size = pf->IDAT_size;
+ ps->IDAT_bits = pf->IDAT_bits; /* just a cache */
+ ps->IDAT_len = 0;
+ ps->IDAT_pos = 0;
+ ps->IDAT_crc = 0UL;
store_read_buffer_next(ps);
return;
}
*/
static png_structp
set_store_for_read(png_store *ps, png_infopp ppi, png_uint_32 id,
- PNG_CONST char *name)
+ const char *name)
{
/* Set the name for png_error */
safecat(ps->test, sizeof ps->test, 0, name);
} color_encoding;
#ifdef PNG_READ_SUPPORTED
+#if defined PNG_READ_TRANSFORMS_SUPPORTED && defined PNG_READ_cHRM_SUPPORTED
static double
chromaticity_x(CIE_color c)
{
}
static CIE_color
-white_point(PNG_CONST color_encoding *encoding)
+white_point(const color_encoding *encoding)
{
CIE_color white;
return white;
}
+#endif /* READ_TRANSFORMS && READ_cHRM */
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
static void
normalize_color_encoding(color_encoding *encoding)
{
- PNG_CONST double whiteY = encoding->red.Y + encoding->green.Y +
+ const double whiteY = encoding->red.Y + encoding->green.Y +
encoding->blue.Y;
if (whiteY != 1)
}
#endif
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
static size_t
safecat_color_encoding(char *buffer, size_t bufsize, size_t pos,
- PNG_CONST color_encoding *e, double encoding_gamma)
+ const color_encoding *e, double encoding_gamma)
{
if (e != 0)
{
return pos;
}
+#endif /* READ_TRANSFORMS */
#endif /* PNG_READ_SUPPORTED */
typedef struct png_modifier
unsigned int ngammas;
unsigned int ngamma_tests; /* Number of gamma tests to run*/
double current_gamma; /* 0 if not set */
- PNG_CONST color_encoding *encodings;
+ const color_encoding *encodings;
unsigned int nencodings;
- PNG_CONST color_encoding *current_encoding; /* If an encoding has been set */
+ const color_encoding *current_encoding; /* If an encoding has been set */
unsigned int encoding_counter; /* For iteration */
int encoding_ignored; /* Something overwrote it */
unsigned int repeat :1; /* Repeat this transform test. */
unsigned int test_uses_encoding :1;
- /* Lowest sbit to test (libpng fails for sbit < 8) */
+ /* Lowest sbit to test (pre-1.7 libpng fails for sbit < 8) */
png_byte sbitlow;
/* Error control - these are the limits on errors accepted by the gamma tests
* internal check on pngvalid to ensure that the calculated error limits are
* not ridiculous; without this it is too easy to make a mistake in pngvalid
* that allows any value through.
+ *
+ * NOTE: this is not checked in release builds.
*/
double limit; /* limit on error values, normally 4E-3 */
/* Run tests on reading with a combination of transforms, */
unsigned int test_transform :1;
+ unsigned int test_tRNS :1; /* Includes tRNS images */
/* When to use the use_input_precision option, this controls the gamma
* validation code checks. If set any value that is within the transformed
unsigned int test_gamma_expand16 :1;
unsigned int test_exhaustive :1;
+ /* Whether or not to run the low-bit-depth grayscale tests. This fails on
+ * gamma images in some cases because of gross inaccuracies in the grayscale
+ * gamma handling for low bit depth.
+ */
+ unsigned int test_lbg :1;
+ unsigned int test_lbg_gamma_threshold :1;
+ unsigned int test_lbg_gamma_transform :1;
+ unsigned int test_lbg_gamma_sbit :1;
+ unsigned int test_lbg_gamma_composition :1;
+
unsigned int log :1; /* Log max error */
/* Buffer information, the buffer size limits the size of the chunks that can
pm->test_standard = 0;
pm->test_size = 0;
pm->test_transform = 0;
+# ifdef PNG_WRITE_tRNS_SUPPORTED
+ pm->test_tRNS = 1;
+# else
+ pm->test_tRNS = 0;
+# endif
pm->use_input_precision = 0;
pm->use_input_precision_sbit = 0;
pm->use_input_precision_16to8 = 0;
pm->test_gamma_background = 0;
pm->test_gamma_alpha_mode = 0;
pm->test_gamma_expand16 = 0;
+ pm->test_lbg = 1;
+ pm->test_lbg_gamma_threshold = 1;
+ pm->test_lbg_gamma_transform = 1;
+ pm->test_lbg_gamma_sbit = 1;
+ pm->test_lbg_gamma_composition = 1;
pm->test_exhaustive = 0;
pm->log = 0;
* rounding and 'do_round' should be 1, if it is 0 the digitized value will
* be truncated.
*/
- PNG_CONST unsigned int digitization_factor = (1U << depth) -1;
+ unsigned int digitization_factor = (1U << depth) - 1;
/* Limiting the range is done as a convenience to the caller - it's easier to
* do it once here than every time at the call site.
#endif /* RGB_TO_GRAY */
#ifdef PNG_READ_GAMMA_SUPPORTED
-static double abserr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
+static double abserr(const png_modifier *pm, int in_depth, int out_depth)
{
/* Absolute error permitted in linear values - affected by the bit depth of
* the calculations.
return pm->maxabs8;
}
-static double calcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
+static double calcerr(const png_modifier *pm, int in_depth, int out_depth)
{
/* Error in the linear composition arithmetic - only relevant when
* composition actually happens (0 < alpha < 1).
return pm->maxcalc8;
}
-static double pcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
+static double pcerr(const png_modifier *pm, int in_depth, int out_depth)
{
/* Percentage error permitted in the linear values. Note that the specified
* value is a percentage but this routine returns a simple number.
* The specified parameter does *not* include the base .5 digitization error but
* it is added here.
*/
-static double outerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
+static double outerr(const png_modifier *pm, int in_depth, int out_depth)
{
/* There is a serious error in the 2 and 4 bit grayscale transform because
* the gamma table value (8 bits) is simply shifted, not rounded, so the
* rather than raising a warning. This is useful for debugging to track down
* exactly what set of parameters cause high error values.
*/
-static double outlog(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
+static double outlog(const png_modifier *pm, int in_depth, int out_depth)
{
/* The command line parameters are either 8 bit (0..255) or 16 bit (0..65535)
* and so must be adjusted for low bit depth grayscale:
* but in the 8 bit calculation case it's actually quantization to a multiple of
* 257!
*/
-static int output_quantization_factor(PNG_CONST png_modifier *pm, int in_depth,
+static int output_quantization_factor(const png_modifier *pm, int in_depth,
int out_depth)
{
if (out_depth == 16 && in_depth != 16 &&
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
static void
-modifier_current_encoding(PNG_CONST png_modifier *pm, color_encoding *ce)
+modifier_current_encoding(const png_modifier *pm, color_encoding *ce)
{
if (pm->current_encoding != 0)
*ce = *pm->current_encoding;
}
#endif
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
static size_t
safecat_current_encoding(char *buffer, size_t bufsize, size_t pos,
- PNG_CONST png_modifier *pm)
+ const png_modifier *pm)
{
pos = safecat_color_encoding(buffer, bufsize, pos, pm->current_encoding,
pm->current_gamma);
return pos;
}
+#endif
/* Iterate through the usefully testable color encodings. An encoding is one
* of:
* caller of modifier_reset must reset it at the start of each run of the test!
*/
static unsigned int
-modifier_total_encodings(PNG_CONST png_modifier *pm)
+modifier_total_encodings(const png_modifier *pm)
{
return 1 + /* (1) nothing */
pm->ngammas + /* (2) gamma values to test */
* assumption below that the first encoding in the list is the one for sRGB.
*/
static int
-modifier_color_encoding_is_sRGB(PNG_CONST png_modifier *pm)
+modifier_color_encoding_is_sRGB(const png_modifier *pm)
{
return pm->current_encoding != 0 && pm->current_encoding == pm->encodings &&
pm->current_encoding->gamma == pm->current_gamma;
}
static int
-modifier_color_encoding_is_set(PNG_CONST png_modifier *pm)
+modifier_color_encoding_is_set(const png_modifier *pm)
{
return pm->current_gamma != 0;
}
-/* Convenience macros. */
-#define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
-#define CHUNK_IHDR CHUNK(73,72,68,82)
-#define CHUNK_PLTE CHUNK(80,76,84,69)
-#define CHUNK_IDAT CHUNK(73,68,65,84)
-#define CHUNK_IEND CHUNK(73,69,78,68)
-#define CHUNK_cHRM CHUNK(99,72,82,77)
-#define CHUNK_gAMA CHUNK(103,65,77,65)
-#define CHUNK_sBIT CHUNK(115,66,73,84)
-#define CHUNK_sRGB CHUNK(115,82,71,66)
-
/* The guts of modification are performed during a read. */
static void
modifier_crc(png_bytep buffer)
* png_struct.
*/
static void
-modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
+modifier_read_imp(png_modifier *pm, png_bytep pb, size_t st)
{
while (st > 0)
{
{
static png_byte sign[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
case modifier_start:
- store_read_imp(&pm->this, pm->buffer, 8); /* size of signature. */
+ store_read_chunk(&pm->this, pm->buffer, 8, 8); /* signature. */
pm->buffer_count = 8;
pm->buffer_position = 0;
break;
case modifier_signature:
- store_read_imp(&pm->this, pm->buffer, 13+12); /* size of IHDR */
+ store_read_chunk(&pm->this, pm->buffer, 13+12, 13+12); /* IHDR */
pm->buffer_count = 13+12;
pm->buffer_position = 0;
{
if (cb > st) cb = st;
pm->flush -= cb;
- store_read_imp(&pm->this, pb, cb);
+ store_read_chunk(&pm->this, pb, cb, cb);
pb += cb;
st -= cb;
if (st == 0) return;
pm->pending_chunk = 0;
}
else
- store_read_imp(&pm->this, pm->buffer, 8);
+ store_read_chunk(&pm->this, pm->buffer, 8, 8);
pm->buffer_count = 8;
pm->buffer_position = 0;
*/
if (len+12 <= sizeof pm->buffer)
{
- store_read_imp(&pm->this, pm->buffer+pm->buffer_count,
- len+12-pm->buffer_count);
+ size_t s = len+12-pm->buffer_count;
+ store_read_chunk(&pm->this, pm->buffer+pm->buffer_count, s, s);
pm->buffer_count = len+12;
/* Check for a modification, else leave it be. */
/* The callback: */
static void PNGCBAPI
-modifier_read(png_structp ppIn, png_bytep pb, png_size_t st)
+modifier_read(png_structp ppIn, png_bytep pb, size_t st)
{
png_const_structp pp = ppIn;
png_modifier *pm = voidcast(png_modifier*, png_get_io_ptr(pp));
for (;;)
{
static png_uint_32 noise = 1;
- png_size_t cb, cbAvail;
+ size_t cb, cbAvail;
png_byte buffer[512];
/* Generate 15 more bits of stuff: */
/* Set up a modifier. */
static png_structp
set_modifier_for_read(png_modifier *pm, png_infopp ppi, png_uint_32 id,
- PNG_CONST char *name)
+ const char *name)
{
/* Do this first so that the modifier fields are cleared even if an error
* happens allocating the png_struct. No allocation is done here so no
typedef struct chrm_modification
{
png_modification this;
- PNG_CONST color_encoding *encoding;
+ const color_encoding *encoding;
png_fixed_point wx, wy, rx, ry, gx, gy, bx, by;
} chrm_modification;
static void
chrm_modification_init(chrm_modification *me, png_modifier *pm,
- PNG_CONST color_encoding *encoding)
+ const color_encoding *encoding)
{
CIE_color white = white_point(encoding);
for (; i<256; ++i)
tRNS[i] = 24;
-# ifdef PNG_WRITE_tRNS_SUPPORTED
- if (j > 0)
- png_set_tRNS(pp, pi, tRNS, j, 0/*color*/);
-# endif
+#ifdef PNG_WRITE_tRNS_SUPPORTED
+ if (j > 0)
+ png_set_tRNS(pp, pi, tRNS, j, 0/*color*/);
+#endif
}
}
-/* The number of passes is related to the interlace type. There was no libpng
- * API to determine this prior to 1.5, so we need an inquiry function:
- */
+#ifdef PNG_WRITE_tRNS_SUPPORTED
+static void
+set_random_tRNS(png_structp pp, png_infop pi, png_byte colour_type,
+ int bit_depth)
+{
+ /* To make this useful the tRNS color needs to match at least one pixel.
+ * Random values are fine for gray, including the 16-bit case where we know
+ * that the test image contains all the gray values. For RGB we need more
+ * method as only 65536 different RGB values are generated.
+ */
+ png_color_16 tRNS;
+ png_uint_16 mask = (png_uint_16)((1U << bit_depth)-1);
+
+ R8(tRNS); /* makes unset fields random */
+
+ if (colour_type & 2/*RGB*/)
+ {
+ if (bit_depth == 8)
+ {
+ tRNS.red = random_u16();
+ tRNS.green = random_u16();
+ tRNS.blue = tRNS.red ^ tRNS.green;
+ tRNS.red &= mask;
+ tRNS.green &= mask;
+ tRNS.blue &= mask;
+ }
+
+ else /* bit_depth == 16 */
+ {
+ tRNS.red = random_u16();
+ tRNS.green = (png_uint_16)(tRNS.red * 257);
+ tRNS.blue = (png_uint_16)(tRNS.green * 17);
+ }
+ }
+
+ else
+ {
+ tRNS.gray = random_u16();
+ tRNS.gray &= mask;
+ }
+
+ png_set_tRNS(pp, pi, NULL, 0, &tRNS);
+}
+#endif
+
+/* The number of passes is related to the interlace type. There was no libpng
+ * API to determine this prior to 1.5, so we need an inquiry function:
+ */
static int
npasses_from_interlace_type(png_const_structp pp, int interlace_type)
{
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
# define INTERLACE_LAST PNG_INTERLACE_LAST
# define check_interlace_type(type) ((void)(type))
-#else
-# define INTERLACE_LAST (PNG_INTERLACE_NONE+1)
-# define png_set_interlace_handling(a) (1)
-
+# define set_write_interlace_handling(pp,type) png_set_interlace_handling(pp)
+# define do_own_interlace 0
+#elif PNG_LIBPNG_VER < 10700
+# define set_write_interlace_handling(pp,type) (1)
static void
-check_interlace_type(int PNG_CONST interlace_type)
+check_interlace_type(int const interlace_type)
{
+ /* Prior to 1.7.0 libpng does not support the write of an interlaced image
+ * unless PNG_WRITE_INTERLACING_SUPPORTED, even with do_interlace so the
+ * code here does the pixel interlace itself, so:
+ */
if (interlace_type != PNG_INTERLACE_NONE)
{
/* This is an internal error - --interlace tests should be skipped, not
exit(99);
}
}
+# define INTERLACE_LAST (PNG_INTERLACE_NONE+1)
+# define do_own_interlace 0
+#else /* libpng 1.7+ */
+# define set_write_interlace_handling(pp,type)\
+ npasses_from_interlace_type(pp,type)
+# define check_interlace_type(type) ((void)(type))
+# define INTERLACE_LAST PNG_INTERLACE_LAST
+# define do_own_interlace 1
+#endif /* WRITE_INTERLACING tests */
+
+#if PNG_LIBPNG_VER >= 10700 || defined PNG_WRITE_INTERLACING_SUPPORTED
+# define CAN_WRITE_INTERLACE 1
+#else
+# define CAN_WRITE_INTERLACE 0
#endif
-/* Make a standardized image given a an image colour type, bit depth and
+/* Do the same thing for read interlacing; this controls whether read tests do
+ * their own de-interlace or use libpng.
+ */
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+# define do_read_interlace 0
+#else /* no libpng read interlace support */
+# define do_read_interlace 1
+#endif
+/* The following two routines use the PNG interlace support macros from
+ * png.h to interlace or deinterlace rows.
+ */
+static void
+interlace_row(png_bytep buffer, png_const_bytep imageRow,
+ unsigned int pixel_size, png_uint_32 w, int pass, int littleendian)
+{
+ png_uint_32 xin, xout, xstep;
+
+ /* Note that this can, trivially, be optimized to a memcpy on pass 7, the
+ * code is presented this way to make it easier to understand. In practice
+ * consult the code in the libpng source to see other ways of doing this.
+ *
+ * It is OK for buffer and imageRow to be identical, because 'xin' moves
+ * faster than 'xout' and we copy up.
+ */
+ xin = PNG_PASS_START_COL(pass);
+ xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
+
+ for (xout=0; xin<w; xin+=xstep)
+ {
+ pixel_copy(buffer, xout, imageRow, xin, pixel_size, littleendian);
+ ++xout;
+ }
+}
+
+#ifdef PNG_READ_SUPPORTED
+static void
+deinterlace_row(png_bytep buffer, png_const_bytep row,
+ unsigned int pixel_size, png_uint_32 w, int pass, int littleendian)
+{
+ /* The inverse of the above, 'row' is part of row 'y' of the output image,
+ * in 'buffer'. The image is 'w' wide and this is pass 'pass', distribute
+ * the pixels of row into buffer and return the number written (to allow
+ * this to be checked).
+ */
+ png_uint_32 xin, xout, xstep;
+
+ xout = PNG_PASS_START_COL(pass);
+ xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
+
+ for (xin=0; xout<w; xout+=xstep)
+ {
+ pixel_copy(buffer, xout, row, xin, pixel_size, littleendian);
+ ++xin;
+ }
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* Make a standardized image given an image colour type, bit depth and
* interlace type. The standard images have a very restricted range of
* rows and heights and are used for testing transforms rather than image
* layout details. See make_size_images below for a way to make images
* that test odd sizes along with the libpng interlace handling.
*/
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+static void
+choose_random_filter(png_structp pp, int start)
+{
+ /* Choose filters randomly except that on the very first row ensure that
+ * there is at least one previous row filter.
+ */
+ int filters = PNG_ALL_FILTERS & random_mod(256U);
+
+ /* There may be no filters; skip the setting. */
+ if (filters != 0)
+ {
+ if (start && filters < PNG_FILTER_UP)
+ filters |= PNG_FILTER_UP;
+
+ png_set_filter(pp, 0/*method*/, filters);
+ }
+}
+#else /* !WRITE_FILTER */
+# define choose_random_filter(pp, start) ((void)0)
+#endif /* !WRITE_FILTER */
+
static void
-make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
- png_byte PNG_CONST bit_depth, unsigned int palette_number,
+make_transform_image(png_store* const ps, png_byte const colour_type,
+ png_byte const bit_depth, unsigned int palette_number,
int interlace_type, png_const_charp name)
{
context(ps, fault);
{
png_infop pi;
png_structp pp = set_store_for_write(ps, &pi, name);
- png_uint_32 h;
+ png_uint_32 h, w;
/* In the event of a problem return control to the Catch statement below
* to do the clean up - it is not possible to 'return' directly from a Try
if (pp == NULL)
Throw ps;
+ w = transform_width(pp, colour_type, bit_depth);
h = transform_height(pp, colour_type, bit_depth);
- png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth), h,
- bit_depth, colour_type, interlace_type,
+ png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
#ifdef PNG_TEXT_SUPPORTED
if (colour_type == 3) /* palette */
init_standard_palette(ps, pp, pi, 1U << bit_depth, 1/*do tRNS*/);
+# ifdef PNG_WRITE_tRNS_SUPPORTED
+ else if (palette_number)
+ set_random_tRNS(pp, pi, colour_type, bit_depth);
+# endif
+
png_write_info(pp, pi);
if (png_get_rowbytes(pp, pi) !=
transform_rowsize(pp, colour_type, bit_depth))
- png_error(pp, "row size incorrect");
+ png_error(pp, "transform row size incorrect");
else
{
* because if it is called before, the information in *pp has not been
* updated to reflect the interlaced image.
*/
- int npasses = png_set_interlace_handling(pp);
+ int npasses = set_write_interlace_handling(pp, interlace_type);
int pass;
if (npasses != npasses_from_interlace_type(pp, interlace_type))
{
png_uint_32 y;
+ /* do_own_interlace is a pre-defined boolean (a #define) which is
+ * set if we have to work out the interlaced rows here.
+ */
for (y=0; y<h; ++y)
{
png_byte buffer[TRANSFORM_ROWMAX];
transform_row(pp, buffer, colour_type, bit_depth, y);
+
+# if do_own_interlace
+ /* If do_own_interlace *and* the image is interlaced we need a
+ * reduced interlace row; this may be reduced to empty.
+ */
+ if (interlace_type == PNG_INTERLACE_ADAM7)
+ {
+ /* The row must not be written if it doesn't exist, notice
+ * that there are two conditions here, either the row isn't
+ * ever in the pass or the row would be but isn't wide
+ * enough to contribute any pixels. In fact the wPass test
+ * can be used to skip the whole y loop in this case.
+ */
+ if (PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
+ PNG_PASS_COLS(w, pass) > 0)
+ interlace_row(buffer, buffer,
+ bit_size(pp, colour_type, bit_depth), w, pass,
+ 0/*data always bigendian*/);
+ else
+ continue;
+ }
+# endif /* do_own_interlace */
+
+ choose_random_filter(pp, pass == 0 && y == 0);
png_write_row(pp, buffer);
}
}
}
static void
-make_transform_images(png_store *ps)
+make_transform_images(png_modifier *pm)
{
png_byte colour_type = 0;
png_byte bit_depth = 0;
unsigned int palette_number = 0;
/* This is in case of errors. */
- safecat(ps->test, sizeof ps->test, 0, "make standard images");
+ safecat(pm->this.test, sizeof pm->this.test, 0, "make standard images");
/* Use next_format to enumerate all the combinations we test, including
- * generating multiple low bit depth palette images.
+ * generating multiple low bit depth palette images. Non-A images (palette
+ * and direct) are created with and without tRNS chunks.
*/
- while (next_format(&colour_type, &bit_depth, &palette_number, 0))
+ while (next_format(&colour_type, &bit_depth, &palette_number, 1, 1))
{
int interlace_type;
char name[FILE_NAME_SIZE];
standard_name(name, sizeof name, 0, colour_type, bit_depth,
- palette_number, interlace_type, 0, 0, 0);
- make_transform_image(ps, colour_type, bit_depth, palette_number,
+ palette_number, interlace_type, 0, 0, do_own_interlace);
+ make_transform_image(&pm->this, colour_type, bit_depth, palette_number,
interlace_type, name);
}
}
}
-/* The following two routines use the PNG interlace support macros from
- * png.h to interlace or deinterlace rows.
- */
-static void
-interlace_row(png_bytep buffer, png_const_bytep imageRow,
- unsigned int pixel_size, png_uint_32 w, int pass)
-{
- png_uint_32 xin, xout, xstep;
-
- /* Note that this can, trivially, be optimized to a memcpy on pass 7, the
- * code is presented this way to make it easier to understand. In practice
- * consult the code in the libpng source to see other ways of doing this.
- */
- xin = PNG_PASS_START_COL(pass);
- xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
-
- for (xout=0; xin<w; xin+=xstep)
- {
- pixel_copy(buffer, xout, imageRow, xin, pixel_size);
- ++xout;
- }
-}
-
-#ifdef PNG_READ_SUPPORTED
-static void
-deinterlace_row(png_bytep buffer, png_const_bytep row,
- unsigned int pixel_size, png_uint_32 w, int pass)
-{
- /* The inverse of the above, 'row' is part of row 'y' of the output image,
- * in 'buffer'. The image is 'w' wide and this is pass 'pass', distribute
- * the pixels of row into buffer and return the number written (to allow
- * this to be checked).
- */
- png_uint_32 xin, xout, xstep;
-
- xout = PNG_PASS_START_COL(pass);
- xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
-
- for (xin=0; xout<w; xout+=xstep)
- {
- pixel_copy(buffer, xout, row, xin, pixel_size);
- ++xin;
- }
-}
-#endif /* PNG_READ_SUPPORTED */
-
/* Build a single row for the 'size' test images; this fills in only the
* first bit_width bits of the sample row.
*/
}
static void
-make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
- png_byte PNG_CONST bit_depth, int PNG_CONST interlace_type,
- png_uint_32 PNG_CONST w, png_uint_32 PNG_CONST h,
- int PNG_CONST do_interlace)
+make_size_image(png_store* const ps, png_byte const colour_type,
+ png_byte const bit_depth, int const interlace_type,
+ png_uint_32 const w, png_uint_32 const h,
+ int const do_interlace)
{
context(ps, fault);
- /* At present libpng does not support the write of an interlaced image unless
- * PNG_WRITE_INTERLACING_SUPPORTED, even with do_interlace so the code here
- * does the pixel interlace itself, so:
- */
check_interlace_type(interlace_type);
Try
/* Make a name and get an appropriate id for the store: */
char name[FILE_NAME_SIZE];
- PNG_CONST png_uint_32 id = FILEID(colour_type, bit_depth, 0/*palette*/,
+ png_uint_32 id = FILEID(colour_type, bit_depth, 0/*palette*/,
interlace_type, w, h, do_interlace);
standard_name_from_id(name, sizeof name, 0, id);
*/
pixel_size = bit_size(pp, colour_type, bit_depth);
if (png_get_rowbytes(pp, pi) != ((w * pixel_size) + 7) / 8)
- png_error(pp, "row size incorrect");
+ png_error(pp, "size row size incorrect");
else
{
int npasses = npasses_from_interlace_type(pp, interlace_type);
png_uint_32 y;
int pass;
-# ifdef PNG_WRITE_FILTER_SUPPORTED
- int nfilter = PNG_FILTER_VALUE_LAST;
-# endif
png_byte image[16][SIZE_ROWMAX];
/* To help consistent error detection make the parts of this buffer
*/
memset(image, 0xff, sizeof image);
- if (!do_interlace && npasses != png_set_interlace_handling(pp))
+ if (!do_interlace &&
+ npasses != set_write_interlace_handling(pp, interlace_type))
png_error(pp, "write: png_set_interlace_handling failed");
/* Prepare the whole image first to avoid making it 7 times: */
for (pass=0; pass<npasses; ++pass)
{
/* The following two are for checking the macros: */
- PNG_CONST png_uint_32 wPass = PNG_PASS_COLS(w, pass);
+ png_uint_32 wPass = PNG_PASS_COLS(w, pass);
/* If do_interlace is set we don't call png_write_row for every
* row because some of them are empty. In fact, for a 1x1 image,
* set unset things to 0).
*/
memset(tempRow, 0xff, sizeof tempRow);
- interlace_row(tempRow, row, pixel_size, w, pass);
+ interlace_row(tempRow, row, pixel_size, w, pass,
+ 0/*data always bigendian*/);
row = tempRow;
}
else
* does accept a filter number (per the spec) as well as a bit
* mask.
*
- * The apparent wackiness of decrementing nfilter rather than
- * incrementing is so that Paeth gets used in all images bigger
- * than 1 row - it's the tricky one.
+ * The code now uses filters at random, except that on the first
+ * row of an image it ensures that a previous row filter is in
+ * the set so that libpng allocates the row buffer.
*/
- png_set_filter(pp, 0/*method*/,
- nfilter >= PNG_FILTER_VALUE_LAST ? PNG_ALL_FILTERS : nfilter);
+ {
+ int filters = 8 << random_mod(PNG_FILTER_VALUE_LAST);
- if (nfilter-- == 0)
- nfilter = PNG_FILTER_VALUE_LAST-1;
+ if (pass == 0 && y == 0 &&
+ (filters < PNG_FILTER_UP || w == 1U))
+ filters |= PNG_FILTER_UP;
+
+ png_set_filter(pp, 0/*method*/, filters);
+ }
# endif
png_write_row(pp, row);
}
static void
-make_size(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo,
- int PNG_CONST bdhi)
+make_size(png_store* const ps, png_byte const colour_type, int bdlo,
+ int const bdhi)
{
for (; bdlo <= bdhi; ++bdlo)
{
# ifdef PNG_WRITE_INTERLACING_SUPPORTED
make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
width, height, 0);
+# endif
+# if CAN_WRITE_INTERLACE
+ /* 1.7.0 removes the hack that prevented app write of an interlaced
+ * image if WRITE_INTERLACE was not supported
+ */
make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
width, height, 1);
# endif
png_set_sBIT(pp, pi, &bad);
}
-static PNG_CONST struct
+static const struct
{
void (*fn)(png_structp, png_infop);
- PNG_CONST char *msg;
+ const char *msg;
unsigned int warning :1; /* the error is a warning... */
} error_test[] =
{
- /* no warnings makes these errors undetectable. */
- { sBIT0_error_fn, "sBIT(0): failed to detect error", 1 },
- { sBIT_error_fn, "sBIT(too big): failed to detect error", 1 },
+ /* no warnings makes these errors undetectable prior to 1.7.0 */
+ { sBIT0_error_fn, "sBIT(0): failed to detect error",
+ PNG_LIBPNG_VER < 10700 },
+
+ { sBIT_error_fn, "sBIT(too big): failed to detect error",
+ PNG_LIBPNG_VER < 10700 },
};
static void
-make_error(png_store* volatile psIn, png_byte PNG_CONST colour_type,
+make_error(png_store* const ps, png_byte const colour_type,
png_byte bit_depth, int interlace_type, int test, png_const_charp name)
{
- png_store * volatile ps = psIn;
-
context(ps, fault);
check_interlace_type(interlace_type);
Try
{
- png_structp pp;
png_infop pi;
-
- pp = set_store_for_write(ps, &pi, name);
+ png_structp pp = set_store_for_write(ps, &pi, name);
+ png_uint_32 w, h;
+ gnu_volatile(pp)
if (pp == NULL)
Throw ps;
- png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth),
- transform_height(pp, colour_type, bit_depth), bit_depth, colour_type,
- interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ w = transform_width(pp, colour_type, bit_depth);
+ gnu_volatile(w)
+ h = transform_height(pp, colour_type, bit_depth);
+ gnu_volatile(h)
+ png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
if (colour_type == 3) /* palette */
init_standard_palette(ps, pp, pi, 1U << bit_depth, 0/*do tRNS*/);
# define exception__env exception_env_1
Try
{
+ gnu_volatile(exception__prev)
+
/* Expect this to throw: */
ps->expect_error = !error_test[test].warning;
ps->expect_warning = error_test[test].warning;
}
Catch (fault)
- ps = fault; /* expected exit, make sure ps is not clobbered */
+ { /* expected exit */
+ }
#undef exception__prev
#undef exception__env
/* And clear these flags */
- ps->expect_error = 0;
ps->expect_warning = 0;
- /* Now write the whole image, just to make sure that the detected, or
- * undetected, errro has not created problems inside libpng.
- */
- if (png_get_rowbytes(pp, pi) !=
- transform_rowsize(pp, colour_type, bit_depth))
- png_error(pp, "row size incorrect");
+ if (ps->expect_error)
+ ps->expect_error = 0;
else
{
- png_uint_32 h = transform_height(pp, colour_type, bit_depth);
- int npasses = png_set_interlace_handling(pp);
- int pass;
-
- if (npasses != npasses_from_interlace_type(pp, interlace_type))
- png_error(pp, "write: png_set_interlace_handling failed");
+ /* Now write the whole image, just to make sure that the detected, or
+ * undetected, error has not created problems inside libpng. This
+ * doesn't work if there was a png_error in png_write_info because that
+ * can abort before PLTE was written.
+ */
+ if (png_get_rowbytes(pp, pi) !=
+ transform_rowsize(pp, colour_type, bit_depth))
+ png_error(pp, "row size incorrect");
- for (pass=0; pass<npasses; ++pass)
+ else
{
- png_uint_32 y;
+ int npasses = set_write_interlace_handling(pp, interlace_type);
+ int pass;
- for (y=0; y<h; ++y)
+ if (npasses != npasses_from_interlace_type(pp, interlace_type))
+ png_error(pp, "write: png_set_interlace_handling failed");
+
+ for (pass=0; pass<npasses; ++pass)
{
- png_byte buffer[TRANSFORM_ROWMAX];
+ png_uint_32 y;
- transform_row(pp, buffer, colour_type, bit_depth, y);
- png_write_row(pp, buffer);
+ for (y=0; y<h; ++y)
+ {
+ png_byte buffer[TRANSFORM_ROWMAX];
+
+ transform_row(pp, buffer, colour_type, bit_depth, y);
+
+# if do_own_interlace
+ /* If do_own_interlace *and* the image is interlaced we
+ * need a reduced interlace row; this may be reduced to
+ * empty.
+ */
+ if (interlace_type == PNG_INTERLACE_ADAM7)
+ {
+ /* The row must not be written if it doesn't exist,
+ * notice that there are two conditions here, either the
+ * row isn't ever in the pass or the row would be but
+ * isn't wide enough to contribute any pixels. In fact
+ * the wPass test can be used to skip the whole y loop
+ * in this case.
+ */
+ if (PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
+ PNG_PASS_COLS(w, pass) > 0)
+ interlace_row(buffer, buffer,
+ bit_size(pp, colour_type, bit_depth), w, pass,
+ 0/*data always bigendian*/);
+ else
+ continue;
+ }
+# endif /* do_own_interlace */
+
+ png_write_row(pp, buffer);
+ }
}
- }
- }
+ } /* image writing */
- png_write_end(pp, pi);
+ png_write_end(pp, pi);
+ }
/* The following deletes the file that was just written. */
store_write_reset(ps);
}
static int
-make_errors(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
- int bdlo, int PNG_CONST bdhi)
+make_errors(png_modifier* const pm, png_byte const colour_type,
+ int bdlo, int const bdhi)
{
for (; bdlo <= bdhi; ++bdlo)
{
char name[FILE_NAME_SIZE];
standard_name(name, sizeof name, 0, colour_type, 1<<bdlo, 0,
- interlace_type, 0, 0, 0);
+ interlace_type, 0, 0, do_own_interlace);
- for (test=0; test<(sizeof error_test)/(sizeof error_test[0]); ++test)
+ for (test=0; test<ARRAY_SIZE(error_test); ++test)
{
make_error(&pm->this, colour_type, DEPTH(bdlo), interlace_type,
test, name);
* then the warning messages the library outputs will probably be garbage.
*/
static void
-perform_formatting_test(png_store *volatile ps)
+perform_formatting_test(png_store *ps)
{
#ifdef PNG_TIME_RFC1123_SUPPORTED
/* The handle into the formatting code is the RFC1123 support; this test does
png_byte green_sBIT;
png_byte blue_sBIT;
png_byte alpha_sBIT;
- int interlace_type;
+ png_byte interlace_type;
+ png_byte filler; /* Output has a filler */
png_uint_32 id; /* Calculated file ID */
png_uint_32 w; /* Width of image */
png_uint_32 h; /* Height of image */
png_uint_32 bit_width; /* Width of output row in bits */
size_t cbRow; /* Bytes in a row of the output image */
int do_interlace; /* Do interlacing internally */
+ int littleendian; /* App (row) data is little endian */
int is_transparent; /* Transparency information was present. */
+ int has_tRNS; /* color type GRAY or RGB with a tRNS chunk. */
int speed; /* Doing a speed test */
int use_update_info;/* Call update_info, not start_image */
struct
dp->bit_width = 0;
dp->cbRow = 0;
dp->do_interlace = do_interlace;
+ dp->littleendian = 0;
dp->is_transparent = 0;
dp->speed = ps->speed;
dp->use_update_info = use_update_info;
dp->npalette = 0;
/* Preset the transparent color to black: */
memset(&dp->transparent, 0, sizeof dp->transparent);
- /* Preset the palette to full intensity/opaque througout: */
+ /* Preset the palette to full intensity/opaque throughout: */
memset(dp->palette, 0xff, sizeof dp->palette);
}
*/
standard_palette_validate(dp, pp, pi);
- /* In any case always check for a tranparent color (notice that the
+ /* In any case always check for a transparent color (notice that the
* colour type 3 case must not give a successful return on the get_tRNS call
* with these arguments!)
*/
case 0:
dp->transparent.red = dp->transparent.green = dp->transparent.blue =
trans_color->gray;
- dp->is_transparent = 1;
+ dp->has_tRNS = 1;
break;
case 2:
dp->transparent.red = trans_color->red;
dp->transparent.green = trans_color->green;
dp->transparent.blue = trans_color->blue;
- dp->is_transparent = 1;
+ dp->has_tRNS = 1;
break;
case 3:
* turning on interlace handling (if do_interlace is not set.)
*/
dp->npasses = npasses_from_interlace_type(pp, dp->interlace_type);
- if (!dp->do_interlace && dp->npasses != png_set_interlace_handling(pp))
- png_error(pp, "validate: file changed interlace type");
+ if (!dp->do_interlace)
+ {
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (dp->npasses != png_set_interlace_handling(pp))
+ png_error(pp, "validate: file changed interlace type");
+# else /* !READ_INTERLACING */
+ /* This should never happen: the relevant tests (!do_interlace) should
+ * not be run.
+ */
+ if (dp->npasses > 1)
+ png_error(pp, "validate: no libpng interlace support");
+# endif /* !READ_INTERLACING */
+ }
/* Caller calls png_read_update_info or png_start_read_image now, then calls
* part2.
png_const_infop pi, int nImages)
{
/* Record cbRow now that it can be found. */
- dp->pixel_size = bit_size(pp, png_get_color_type(pp, pi),
- png_get_bit_depth(pp, pi));
+ {
+ png_byte ct = png_get_color_type(pp, pi);
+ png_byte bd = png_get_bit_depth(pp, pi);
+
+ if (bd >= 8 && (ct == PNG_COLOR_TYPE_RGB || ct == PNG_COLOR_TYPE_GRAY) &&
+ dp->filler)
+ ct |= 4; /* handle filler as faked alpha channel */
+
+ dp->pixel_size = bit_size(pp, ct, bd);
+ }
dp->bit_width = png_get_image_width(pp, pi) * dp->pixel_size;
dp->cbRow = png_get_rowbytes(pp, pi);
progressive_row(png_structp ppIn, png_bytep new_row, png_uint_32 y, int pass)
{
png_const_structp pp = ppIn;
- PNG_CONST standard_display *dp = voidcast(standard_display*,
+ const standard_display *dp = voidcast(standard_display*,
png_get_progressive_ptr(pp));
/* When handling interlacing some rows will be absent in each pass, the
if (pass != png_get_current_pass_number(pp))
png_error(pp, "png_get_current_pass_number is broken");
-#endif
+#endif /* USER_TRANSFORM_INFO */
y = PNG_ROW_FROM_PASS_ROW(y, pass);
}
row = store_image_row(dp->ps, pp, 0, y);
-#ifdef PNG_READ_INTERLACING_SUPPORTED
/* Combine the new row into the old: */
+#ifdef PNG_READ_INTERLACING_SUPPORTED
if (dp->do_interlace)
+#endif /* READ_INTERLACING */
{
if (dp->interlace_type == PNG_INTERLACE_ADAM7)
- deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass);
+ deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass,
+ dp->littleendian);
else
- row_copy(row, new_row, dp->pixel_size * dp->w);
+ row_copy(row, new_row, dp->pixel_size * dp->w, dp->littleendian);
}
+#ifdef PNG_READ_INTERLACING_SUPPORTED
else
png_progressive_combine_row(pp, row, new_row);
#endif /* PNG_READ_INTERLACING_SUPPORTED */
}
-#ifdef PNG_READ_INTERLACING_SUPPORTED
else if (dp->interlace_type == PNG_INTERLACE_ADAM7 &&
PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
PNG_PASS_COLS(dp->w, pass) > 0)
png_error(pp, "missing row in progressive de-interlacing");
-#endif /* PNG_READ_INTERLACING_SUPPORTED */
}
static void
sequential_row(standard_display *dp, png_structp pp, png_infop pi,
- PNG_CONST int iImage, PNG_CONST int iDisplay)
+ int iImage, int iDisplay)
{
- PNG_CONST int npasses = dp->npasses;
- PNG_CONST int do_interlace = dp->do_interlace &&
+ int npasses = dp->npasses;
+ int do_interlace = dp->do_interlace &&
dp->interlace_type == PNG_INTERLACE_ADAM7;
- PNG_CONST png_uint_32 height = standard_height(pp, dp->id);
- PNG_CONST png_uint_32 width = standard_width(pp, dp->id);
- PNG_CONST png_store* ps = dp->ps;
+ png_uint_32 height = standard_height(pp, dp->id);
+ png_uint_32 width = standard_width(pp, dp->id);
+ const png_store* ps = dp->ps;
int pass;
for (pass=0; pass<npasses; ++pass)
if (iImage >= 0)
deinterlace_row(store_image_row(ps, pp, iImage, y), row,
- dp->pixel_size, dp->w, pass);
+ dp->pixel_size, dp->w, pass, dp->littleendian);
if (iDisplay >= 0)
deinterlace_row(store_image_row(ps, pp, iDisplay, y), display,
- dp->pixel_size, dp->w, pass);
+ dp->pixel_size, dp->w, pass, dp->littleendian);
}
}
else
* In earlier passes 'row' will be partially filled in, with only the pixels
* that have been read so far, but 'display' will have those pixels
* replicated to fill the unread pixels while reading an interlaced image.
-#if PNG_LIBPNG_VER < 10506
- * The side effect inside the libpng sequential reader is that the 'row'
- * array retains the correct values for unwritten pixels within the row
- * bytes, while the 'display' array gets bits off the end of the image (in
- * the last byte) trashed. Unfortunately in the progressive reader the
- * row bytes are always trashed, so we always do a pixel_cmp here even though
- * a memcmp of all cbRow bytes will succeed for the sequential reader.
-#endif
*/
if (iImage >= 0 &&
(where = pixel_cmp(std, store_image_row(dp->ps, pp, iImage, y),
png_error(pp, msg);
}
-#if PNG_LIBPNG_VER < 10506
- /* In this case use pixel_cmp because we need to compare a partial
- * byte at the end of the row if the row is not an exact multiple
- * of 8 bits wide. (This is fixed in libpng-1.5.6 and pixel_cmp is
- * changed to match!)
- */
-#endif
if (iDisplay >= 0 &&
(where = pixel_cmp(std, store_image_row(dp->ps, pp, iDisplay, y),
dp->bit_width)) != 0)
{
char msg[64];
- sprintf(msg, "display row[%lu][%d] changed from %.2x to %.2x",
+ sprintf(msg, "display row[%lu][%d] changed from %.2x to %.2x",
(unsigned long)y, where-1, std[where-1],
store_image_row(dp->ps, pp, iDisplay, y)[where-1]);
png_error(pp, msg);
/* A single test run checking the standard image to ensure it is not damaged. */
static void
-standard_test(png_store* PNG_CONST psIn, png_uint_32 PNG_CONST id,
+standard_test(png_store* const psIn, png_uint_32 const id,
int do_interlace, int use_update_info)
{
standard_display d;
}
static int
-test_standard(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
- int bdlo, int PNG_CONST bdhi)
+test_standard(png_modifier* const pm, png_byte const colour_type,
+ int bdlo, int const bdhi)
{
for (; bdlo <= bdhi; ++bdlo)
{
for (interlace_type = PNG_INTERLACE_NONE;
interlace_type < INTERLACE_LAST; ++interlace_type)
{
- standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
- interlace_type, 0, 0, 0), 0/*do_interlace*/, pm->use_update_info);
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ 0/*palette*/, interlace_type, 0, 0, 0), do_read_interlace,
+ pm->use_update_info);
if (fail(pm))
return 0;
/********************************** SIZE TESTS ********************************/
static int
-test_size(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
- int bdlo, int PNG_CONST bdhi)
+test_size(png_modifier* const pm, png_byte const colour_type,
+ int bdlo, int const bdhi)
{
/* Run the tests on each combination.
*
* width and height. This is a waste of time in practice, hence the
* hinc and winc stuff:
*/
- static PNG_CONST png_byte hinc[] = {1, 3, 11, 1, 5};
- static PNG_CONST png_byte winc[] = {1, 9, 5, 7, 1};
+ static const png_byte hinc[] = {1, 3, 11, 1, 5};
+ static const png_byte winc[] = {1, 9, 5, 7, 1};
+ int save_bdlo = bdlo;
+
for (; bdlo <= bdhi; ++bdlo)
{
png_uint_32 h, w;
- for (h=1; h<=16; h+=hinc[bdlo]) for (w=1; w<=16; w+=winc[bdlo])
+ for (h=1; h<=16; h+=hinc[bdlo])
{
- /* First test all the 'size' images against the sequential
- * reader using libpng to deinterlace (where required.) This
- * validates the write side of libpng. There are four possibilities
- * to validate.
- */
- standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
- PNG_INTERLACE_NONE, w, h, 0), 0/*do_interlace*/,
- pm->use_update_info);
+ for (w=1; w<=16; w+=winc[bdlo])
+ {
+ /* First test all the 'size' images against the sequential
+ * reader using libpng to deinterlace (where required.) This
+ * validates the write side of libpng. There are four possibilities
+ * to validate.
+ */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ 0/*palette*/, PNG_INTERLACE_NONE, w, h, 0), 0/*do_interlace*/,
+ pm->use_update_info);
- if (fail(pm))
- return 0;
+ if (fail(pm))
+ return 0;
- standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
- PNG_INTERLACE_NONE, w, h, 1), 0/*do_interlace*/,
- pm->use_update_info);
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ 0/*palette*/, PNG_INTERLACE_NONE, w, h, 1), 0/*do_interlace*/,
+ pm->use_update_info);
- if (fail(pm))
- return 0;
+ if (fail(pm))
+ return 0;
-# ifdef PNG_WRITE_INTERLACING_SUPPORTED
- standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
- PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/,
- pm->use_update_info);
+ /* Now validate the interlaced read side - do_interlace true,
+ * in the progressive case this does actually make a difference
+ * to the code used in the non-interlaced case too.
+ */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ 0/*palette*/, PNG_INTERLACE_NONE, w, h, 0), 1/*do_interlace*/,
+ pm->use_update_info);
- if (fail(pm))
- return 0;
+ if (fail(pm))
+ return 0;
- standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
- PNG_INTERLACE_ADAM7, w, h, 1), 0/*do_interlace*/,
- pm->use_update_info);
+# if CAN_WRITE_INTERLACE
+ /* Validate the pngvalid code itself: */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ 0/*palette*/, PNG_INTERLACE_ADAM7, w, h, 1), 1/*do_interlace*/,
+ pm->use_update_info);
- if (fail(pm))
- return 0;
-# endif
+ if (fail(pm))
+ return 0;
+# endif
+ }
+ }
+ }
- /* Now validate the interlaced read side - do_interlace true,
- * in the progressive case this does actually make a difference
- * to the code used in the non-interlaced case too.
- */
- standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
- PNG_INTERLACE_NONE, w, h, 0), 1/*do_interlace*/,
- pm->use_update_info);
+ /* Now do the tests of libpng interlace handling, after we have made sure
+ * that the pngvalid version works:
+ */
+ for (bdlo = save_bdlo; bdlo <= bdhi; ++bdlo)
+ {
+ png_uint_32 h, w;
- if (fail(pm))
- return 0;
+ for (h=1; h<=16; h+=hinc[bdlo])
+ {
+ for (w=1; w<=16; w+=winc[bdlo])
+ {
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ /* Test with pngvalid generated interlaced images first; we have
+ * already verify these are ok (unless pngvalid has self-consistent
+ * read/write errors, which is unlikely), so this detects errors in
+ * the read side first:
+ */
+# if CAN_WRITE_INTERLACE
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ 0/*palette*/, PNG_INTERLACE_ADAM7, w, h, 1), 0/*do_interlace*/,
+ pm->use_update_info);
-# ifdef PNG_WRITE_INTERLACING_SUPPORTED
- standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
- PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/,
- pm->use_update_info);
+ if (fail(pm))
+ return 0;
+# endif
+# endif /* READ_INTERLACING */
- if (fail(pm))
- return 0;
-# endif
- }
- }
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* Test the libpng write side against the pngvalid read side: */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ 0/*palette*/, PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/,
+ pm->use_update_info);
- return 1; /* keep going */
+ if (fail(pm))
+ return 0;
+# endif
+
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* Test both together: */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ 0/*palette*/, PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/,
+ pm->use_update_info);
+
+ if (fail(pm))
+ return 0;
+# endif
+# endif /* READ_INTERLACING */
+ }
+ }
+ }
+
+ return 1; /* keep going */
}
static void
*/
unsigned int red, green, blue, alpha; /* For non-palette images. */
unsigned int palette_index; /* For a palette image. */
- png_byte colour_type; /* As in the spec. */
- png_byte bit_depth; /* Defines bit size in row */
- png_byte sample_depth; /* Scale of samples */
- int have_tRNS; /* tRNS chunk may need processing */
+ png_byte colour_type; /* As in the spec. */
+ png_byte bit_depth; /* Defines bit size in row */
+ png_byte sample_depth; /* Scale of samples */
+ unsigned int have_tRNS :1; /* tRNS chunk may need processing */
+ unsigned int swap_rgb :1; /* RGB swapped to BGR */
+ unsigned int alpha_first :1; /* Alpha at start, not end */
+ unsigned int alpha_inverted :1; /* Alpha channel inverted */
+ unsigned int mono_inverted :1; /* Gray channel inverted */
+ unsigned int swap16 :1; /* Byte swap 16-bit components */
+ unsigned int littleendian :1; /* High bits on right */
+ unsigned int sig_bits :1; /* Pixel shifted (sig bits only) */
/* For checking the code calculates double precision floating point values
* along with an error value, accumulated from the transforms. Because an
* up to just less than +/-1 in the scaled value) the *lowest* sBIT for each
* channel is stored. This sBIT value is folded in to the stored error value
* at the end of the application of the transforms to the pixel.
+ *
+ * If sig_bits is set above the red, green, blue and alpha values have been
+ * scaled so they only contain the significant bits of the component values.
*/
double redf, greenf, bluef, alphaf;
double rede, greene, bluee, alphae;
/* Shared utility function, see below. */
static void
-image_pixel_setf(image_pixel *this, unsigned int max)
+image_pixel_setf(image_pixel *this, unsigned int rMax, unsigned int gMax,
+ unsigned int bMax, unsigned int aMax)
{
- this->redf = this->red / (double)max;
- this->greenf = this->green / (double)max;
- this->bluef = this->blue / (double)max;
- this->alphaf = this->alpha / (double)max;
+ this->redf = this->red / (double)rMax;
+ this->greenf = this->green / (double)gMax;
+ this->bluef = this->blue / (double)bMax;
+ this->alphaf = this->alpha / (double)aMax;
- if (this->red < max)
+ if (this->red < rMax)
this->rede = this->redf * DBL_EPSILON;
else
this->rede = 0;
- if (this->green < max)
+ if (this->green < gMax)
this->greene = this->greenf * DBL_EPSILON;
else
this->greene = 0;
- if (this->blue < max)
+ if (this->blue < bMax)
this->bluee = this->bluef * DBL_EPSILON;
else
this->bluee = 0;
- if (this->alpha < max)
+ if (this->alpha < aMax)
this->alphae = this->alphaf * DBL_EPSILON;
else
this->alphae = 0;
*/
static void
image_pixel_init(image_pixel *this, png_const_bytep row, png_byte colour_type,
- png_byte bit_depth, png_uint_32 x, store_palette palette)
+ png_byte bit_depth, png_uint_32 x, store_palette palette,
+ const image_pixel *format /*from pngvalid transform of input*/)
{
- PNG_CONST png_byte sample_depth = (png_byte)(colour_type ==
- PNG_COLOR_TYPE_PALETTE ? 8 : bit_depth);
- PNG_CONST unsigned int max = (1U<<sample_depth)-1;
+ png_byte sample_depth =
+ (png_byte)(colour_type == PNG_COLOR_TYPE_PALETTE ? 8 : bit_depth);
+ unsigned int max = (1U<<sample_depth)-1;
+ int swap16 = (format != 0 && format->swap16);
+ int littleendian = (format != 0 && format->littleendian);
+ int sig_bits = (format != 0 && format->sig_bits);
/* Initially just set everything to the same number and the alpha to opaque.
* Note that this currently assumes a simple palette where entry x has colour
* rgb(x,x,x)!
*/
this->palette_index = this->red = this->green = this->blue =
- sample(row, colour_type, bit_depth, x, 0);
+ sample(row, colour_type, bit_depth, x, 0, swap16, littleendian);
this->alpha = max;
this->red_sBIT = this->green_sBIT = this->blue_sBIT = this->alpha_sBIT =
sample_depth;
/* This permits the caller to default to the sample value. */
if (palette != 0)
{
- PNG_CONST unsigned int i = this->palette_index;
+ unsigned int i = this->palette_index;
this->red = palette[i].red;
this->green = palette[i].green;
{
unsigned int i = 0;
+ if ((colour_type & 4) != 0 && format != 0 && format->alpha_first)
+ {
+ this->alpha = this->red;
+ /* This handles the gray case for 'AG' pixels */
+ this->palette_index = this->red = this->green = this->blue =
+ sample(row, colour_type, bit_depth, x, 1, swap16, littleendian);
+ i = 1;
+ }
+
if (colour_type & 2)
{
- this->green = sample(row, colour_type, bit_depth, x, 1);
- this->blue = sample(row, colour_type, bit_depth, x, 2);
- i = 2;
+ /* Green is second for both BGR and RGB: */
+ this->green = sample(row, colour_type, bit_depth, x, ++i, swap16,
+ littleendian);
+
+ if (format != 0 && format->swap_rgb) /* BGR */
+ this->red = sample(row, colour_type, bit_depth, x, ++i, swap16,
+ littleendian);
+ else
+ this->blue = sample(row, colour_type, bit_depth, x, ++i, swap16,
+ littleendian);
+ }
+
+ else /* grayscale */ if (format != 0 && format->mono_inverted)
+ this->red = this->green = this->blue = this->red ^ max;
+
+ if ((colour_type & 4) != 0) /* alpha */
+ {
+ if (format == 0 || !format->alpha_first)
+ this->alpha = sample(row, colour_type, bit_depth, x, ++i, swap16,
+ littleendian);
+
+ if (format != 0 && format->alpha_inverted)
+ this->alpha ^= max;
}
- if (colour_type & 4)
- this->alpha = sample(row, colour_type, bit_depth, x, ++i);
}
/* Calculate the scaled values, these are simply the values divided by
* 'max' and the error is initialized to the double precision epsilon value
* from the header file.
*/
- image_pixel_setf(this, max);
+ image_pixel_setf(this,
+ sig_bits ? (1U << format->red_sBIT)-1 : max,
+ sig_bits ? (1U << format->green_sBIT)-1 : max,
+ sig_bits ? (1U << format->blue_sBIT)-1 : max,
+ sig_bits ? (1U << format->alpha_sBIT)-1 : max);
/* Store the input information for use in the transforms - these will
* modify the information.
this->bit_depth = bit_depth;
this->sample_depth = sample_depth;
this->have_tRNS = 0;
-}
-
+ this->swap_rgb = 0;
+ this->alpha_first = 0;
+ this->alpha_inverted = 0;
+ this->mono_inverted = 0;
+ this->swap16 = 0;
+ this->littleendian = 0;
+ this->sig_bits = 0;
+}
+
+#if defined PNG_READ_EXPAND_SUPPORTED || defined PNG_READ_GRAY_TO_RGB_SUPPORTED\
+ || defined PNG_READ_EXPAND_SUPPORTED || defined PNG_READ_EXPAND_16_SUPPORTED\
+ || defined PNG_READ_BACKGROUND_SUPPORTED
/* Convert a palette image to an rgb image. This necessarily converts the tRNS
* chunk at the same time, because the tRNS will be in palette form. The way
* palette validation works means that the original palette is never updated,
/* Add an alpha channel; this will import the tRNS information because tRNS is
* not valid in an alpha image. The bit depth will invariably be set to at
- * least 8. Palette images will be converted to alpha (using the above API).
+ * least 8 prior to 1.7.0. Palette images will be converted to alpha (using
+ * the above API). With png_set_background the alpha channel is never expanded
+ * but this routine is used by pngvalid to simplify code; 'for_background'
+ * records this.
*/
static void
-image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display)
+image_pixel_add_alpha(image_pixel *this, const standard_display *display,
+ int for_background)
{
if (this->colour_type == PNG_COLOR_TYPE_PALETTE)
image_pixel_convert_PLTE(this);
{
if (this->colour_type == PNG_COLOR_TYPE_GRAY)
{
- if (this->bit_depth < 8)
- this->bit_depth = 8;
+# if PNG_LIBPNG_VER < 10700
+ if (!for_background && this->bit_depth < 8)
+ this->bit_depth = this->sample_depth = 8;
+# endif
if (this->have_tRNS)
{
+ /* After 1.7 the expansion of bit depth only happens if there is a
+ * tRNS chunk to expand at this point.
+ */
+# if PNG_LIBPNG_VER >= 10700
+ if (!for_background && this->bit_depth < 8)
+ this->bit_depth = this->sample_depth = 8;
+# endif
+
this->have_tRNS = 0;
/* Check the input, original, channel value here against the
this->alphaf = 0;
else
this->alphaf = 1;
-
- this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
}
+ else
+ this->alphaf = 1;
+
+ this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
}
/* The error in the alpha is zero and the sBIT value comes from the
this->alpha_sBIT = display->alpha_sBIT;
}
}
+#endif /* transforms that need image_pixel_add_alpha */
struct transform_display;
typedef struct image_transform
{
/* The name of this transform: a string. */
- PNG_CONST char *name;
+ const char *name;
/* Each transform can be disabled from the command line: */
int enable;
/* The global list of transforms; read only. */
- struct image_transform *PNG_CONST list;
+ struct image_transform *const list;
/* The global count of the number of times this transform has been set on an
* image.
/* The next transform in the list, each transform must call its own next
* transform after it has processed the pixel successfully.
*/
- PNG_CONST struct image_transform *next;
+ const struct image_transform *next;
/* A single transform for the image, expressed as a series of function
* callbacks and some space for values.
* First a callback to add any required modifications to the png_modifier;
* this gets called just before the modifier is set up for read.
*/
- void (*ini)(PNG_CONST struct image_transform *this,
+ void (*ini)(const struct image_transform *this,
struct transform_display *that);
/* And a callback to set the transform on the current png_read_struct:
*/
- void (*set)(PNG_CONST struct image_transform *this,
+ void (*set)(const struct image_transform *this,
struct transform_display *that, png_structp pp, png_infop pi);
/* Then a transform that takes an input pixel in one PNG format or another
* in the libpng implementation!) The png_structp is solely to allow error
* reporting via png_error and png_warning.
*/
- void (*mod)(PNG_CONST struct image_transform *this, image_pixel *that,
- png_const_structp pp, PNG_CONST struct transform_display *display);
+ void (*mod)(const struct image_transform *this, image_pixel *that,
+ png_const_structp pp, const struct transform_display *display);
/* Add this transform to the list and return true if the transform is
* meaningful for this colour type and bit depth - if false then the
* point running it.
*/
int (*add)(struct image_transform *this,
- PNG_CONST struct image_transform **that, png_byte colour_type,
+ const struct image_transform **that, png_byte colour_type,
png_byte bit_depth);
} image_transform;
/* Parameters */
png_modifier* pm;
- PNG_CONST image_transform* transform_list;
+ const image_transform* transform_list;
+ unsigned int max_gamma_8;
/* Local variables */
png_byte output_colour_type;
png_byte output_bit_depth;
+ png_byte unpacked;
/* Modifications (not necessarily used.) */
gama_modification gama_mod;
/* Three functions to end the list: */
static void
-image_transform_ini_end(PNG_CONST image_transform *this,
+image_transform_ini_end(const image_transform *this,
transform_display *that)
{
UNUSED(this)
}
static void
-image_transform_set_end(PNG_CONST image_transform *this,
+image_transform_set_end(const image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
UNUSED(this)
}
static void
-image_transform_mod_end(PNG_CONST image_transform *this, image_pixel *that,
- png_const_structp pp, PNG_CONST transform_display *display)
+image_transform_mod_end(const image_transform *this, image_pixel *that,
+ png_const_structp pp, const transform_display *display)
{
- PNG_CONST unsigned int scale = (1U<<that->sample_depth)-1;
+ unsigned int scale = (1U<<that->sample_depth)-1;
+ int sig_bits = that->sig_bits;
UNUSED(this)
UNUSED(pp)
*/
that->red = sample_scale(that->redf, scale);
+ /* This is a bit bogus; really the above calculation should use the red_sBIT
+ * value, not sample_depth, but because libpng does png_set_shift by just
+ * shifting the bits we get errors if we don't do it the same way.
+ */
+ if (sig_bits && that->red_sBIT < that->sample_depth)
+ that->red >>= that->sample_depth - that->red_sBIT;
+
/* The error value is increased, at the end, according to the lowest sBIT
* value seen. Common sense tells us that the intermediate integer
* representations are no more accurate than +/- 0.5 in the integral values,
if (that->colour_type & PNG_COLOR_MASK_COLOR)
{
that->green = sample_scale(that->greenf, scale);
+ if (sig_bits && that->green_sBIT < that->sample_depth)
+ that->green >>= that->sample_depth - that->green_sBIT;
+
that->blue = sample_scale(that->bluef, scale);
+ if (sig_bits && that->blue_sBIT < that->sample_depth)
+ that->blue >>= that->sample_depth - that->blue_sBIT;
+
that->greene += 1./(2*((1U<<that->green_sBIT)-1));
that->bluee += 1./(2*((1U<<that->blue_sBIT)-1));
}
else
{
that->alpha = scale; /* opaque */
- that->alpha = 1; /* Override this. */
+ that->alphaf = 1; /* Override this. */
that->alphae = 0; /* It's exact ;-) */
}
+
+ if (sig_bits && that->alpha_sBIT < that->sample_depth)
+ that->alpha >>= that->sample_depth - that->alpha_sBIT;
}
/* Static 'end' structure: */
*/
static void
transform_display_init(transform_display *dp, png_modifier *pm, png_uint_32 id,
- PNG_CONST image_transform *transform_list)
+ const image_transform *transform_list)
{
memset(dp, 0, sizeof *dp);
/* Standard fields */
- standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/,
+ standard_display_init(&dp->this, &pm->this, id, do_read_interlace,
pm->use_update_info);
/* Parameter fields */
dp->pm = pm;
dp->transform_list = transform_list;
+ dp->max_gamma_8 = 16;
/* Local variable fields */
dp->output_colour_type = 255; /* invalid */
dp->output_bit_depth = 255; /* invalid */
+ dp->unpacked = 0; /* not unpacked */
}
static void
dp->output_colour_type = png_get_color_type(pp, pi);
dp->output_bit_depth = png_get_bit_depth(pp, pi);
+ /* If png_set_filler is in action then fake the output color type to include
+ * an alpha channel where appropriate.
+ */
+ if (dp->output_bit_depth >= 8 &&
+ (dp->output_colour_type == PNG_COLOR_TYPE_RGB ||
+ dp->output_colour_type == PNG_COLOR_TYPE_GRAY) && dp->this.filler)
+ dp->output_colour_type |= 4;
+
/* Validate the combination of colour type and bit depth that we are getting
* out of libpng; the semantics of something not in the PNG spec are, at
* best, unclear.
{
case PNG_COLOR_TYPE_PALETTE:
if (dp->output_bit_depth > 8) goto error;
- /*FALL THROUGH*/
+ /* FALLTHROUGH */
case PNG_COLOR_TYPE_GRAY:
if (dp->output_bit_depth == 1 || dp->output_bit_depth == 2 ||
dp->output_bit_depth == 4)
break;
- /*FALL THROUGH*/
+ /* FALLTHROUGH */
default:
if (dp->output_bit_depth == 8 || dp->output_bit_depth == 16)
break;
- /*FALL THROUGH*/
+ /* FALLTHROUGH */
error:
{
char message[128];
}
/* Use a test pixel to check that the output agrees with what we expect -
- * this avoids running the whole test if the output is unexpected.
+ * this avoids running the whole test if the output is unexpected. This also
+ * checks for internal errors.
*/
{
image_pixel test_pixel;
/* Don't need sBIT here, but it must be set to non-zero to avoid
* arithmetic overflows.
*/
- test_pixel.have_tRNS = dp->this.is_transparent;
+ test_pixel.have_tRNS = dp->this.is_transparent != 0;
test_pixel.red_sBIT = test_pixel.green_sBIT = test_pixel.blue_sBIT =
test_pixel.alpha_sBIT = test_pixel.sample_depth;
}
/* If both bit depth and colour type are correct check the sample depth.
- * I believe these are both internal errors.
*/
- if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE)
+ if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE &&
+ test_pixel.sample_depth != 8) /* oops - internal error! */
+ png_error(pp, "pngvalid: internal: palette sample depth not 8");
+ else if (dp->unpacked && test_pixel.bit_depth != 8)
+ png_error(pp, "pngvalid: internal: bad unpacked pixel depth");
+ else if (!dp->unpacked && test_pixel.colour_type != PNG_COLOR_TYPE_PALETTE
+ && test_pixel.bit_depth != test_pixel.sample_depth)
{
- if (test_pixel.sample_depth != 8) /* oops - internal error! */
- png_error(pp, "pngvalid: internal: palette sample depth not 8");
+ char message[128];
+ size_t pos = safecat(message, sizeof message, 0,
+ "internal: sample depth ");
+
+ /* Because unless something has set 'unpacked' or the image is palette
+ * mapped we expect the transform to keep sample depth and bit depth
+ * the same.
+ */
+ pos = safecatn(message, sizeof message, pos, test_pixel.sample_depth);
+ pos = safecat(message, sizeof message, pos, " expected ");
+ pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth);
+
+ png_error(pp, message);
}
- else if (test_pixel.sample_depth != dp->output_bit_depth)
+ else if (test_pixel.bit_depth != dp->output_bit_depth)
{
+ /* This could be a libpng error too; libpng has not produced what we
+ * expect for the output bit depth.
+ */
char message[128];
size_t pos = safecat(message, sizeof message, 0,
- "internal: sample depth ");
+ "internal: bit depth ");
pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
pos = safecat(message, sizeof message, pos, " expected ");
- pos = safecatn(message, sizeof message, pos, test_pixel.sample_depth);
+ pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth);
png_error(pp, message);
}
transform_range_check(png_const_structp pp, unsigned int r, unsigned int g,
unsigned int b, unsigned int a, unsigned int in_digitized, double in,
unsigned int out, png_byte sample_depth, double err, double limit,
- PNG_CONST char *name, double digitization_error)
+ const char *name, double digitization_error)
{
- /* Compare the scaled, digitzed, values of our local calculation (in+-err)
+ /* Compare the scaled, digitized, values of our local calculation (in+-err)
* with the digitized values libpng produced; 'sample_depth' is the actual
* digitization depth of the libpng output colors (the bit depth except for
* palette images where it is always 8.) The check on 'err' is to detect
unsigned int max = (1U<<sample_depth)-1;
double in_min = ceil((in-err)*max - digitization_error);
double in_max = floor((in+err)*max + digitization_error);
- if (err > limit || !(out >= in_min && out <= in_max))
+ if (debugonly(err > limit ||) !(out >= in_min && out <= in_max))
{
char message[256];
size_t pos;
png_error(pp, message);
}
+
+ UNUSED(limit)
}
static void
png_infop pi)
{
/* Constants for the loop below: */
- PNG_CONST png_store* PNG_CONST ps = dp->this.ps;
- PNG_CONST png_byte in_ct = dp->this.colour_type;
- PNG_CONST png_byte in_bd = dp->this.bit_depth;
- PNG_CONST png_uint_32 w = dp->this.w;
- PNG_CONST png_uint_32 h = dp->this.h;
- PNG_CONST png_byte out_ct = dp->output_colour_type;
- PNG_CONST png_byte out_bd = dp->output_bit_depth;
- PNG_CONST png_byte sample_depth = (png_byte)(out_ct ==
- PNG_COLOR_TYPE_PALETTE ? 8 : out_bd);
- PNG_CONST png_byte red_sBIT = dp->this.red_sBIT;
- PNG_CONST png_byte green_sBIT = dp->this.green_sBIT;
- PNG_CONST png_byte blue_sBIT = dp->this.blue_sBIT;
- PNG_CONST png_byte alpha_sBIT = dp->this.alpha_sBIT;
- PNG_CONST int have_tRNS = dp->this.is_transparent;
+ const png_store* const ps = dp->this.ps;
+ png_byte in_ct = dp->this.colour_type;
+ png_byte in_bd = dp->this.bit_depth;
+ png_uint_32 w = dp->this.w;
+ png_uint_32 h = dp->this.h;
+ png_byte out_ct = dp->output_colour_type;
+ png_byte out_bd = dp->output_bit_depth;
+ png_byte sample_depth =
+ (png_byte)(out_ct == PNG_COLOR_TYPE_PALETTE ? 8 : out_bd);
+ png_byte red_sBIT = dp->this.red_sBIT;
+ png_byte green_sBIT = dp->this.green_sBIT;
+ png_byte blue_sBIT = dp->this.blue_sBIT;
+ png_byte alpha_sBIT = dp->this.alpha_sBIT;
+ int have_tRNS = dp->this.is_transparent;
double digitization_error;
store_palette out_palette;
store_image_check(dp->this.ps, pp, 0);
/* Read the palette corresponding to the output if the output colour type
- * indicates a palette, othewise set out_palette to garbage.
+ * indicates a palette, otherwise set out_palette to garbage.
*/
if (out_ct == PNG_COLOR_TYPE_PALETTE)
{
for (y=0; y<h; ++y)
{
- png_const_bytep PNG_CONST pRow = store_image_row(ps, pp, 0, y);
+ png_const_bytep const pRow = store_image_row(ps, pp, 0, y);
png_uint_32 x;
/* The original, standard, row pre-transforms. */
unsigned int r, g, b, a;
/* Find out what we think the pixel should be: */
- image_pixel_init(&in_pixel, std, in_ct, in_bd, x, dp->this.palette);
+ image_pixel_init(&in_pixel, std, in_ct, in_bd, x, dp->this.palette,
+ NULL);
in_pixel.red_sBIT = red_sBIT;
in_pixel.green_sBIT = green_sBIT;
in_pixel.blue_sBIT = blue_sBIT;
in_pixel.alpha_sBIT = alpha_sBIT;
- in_pixel.have_tRNS = have_tRNS;
+ in_pixel.have_tRNS = have_tRNS != 0;
/* For error detection, below. */
r = in_pixel.red;
b = in_pixel.blue;
a = in_pixel.alpha;
+ /* This applies the transforms to the input data, including output
+ * format operations which must be used when reading the output
+ * pixel that libpng produces.
+ */
dp->transform_list->mod(dp->transform_list, &in_pixel, pp, dp);
/* Read the output pixel and compare it to what we got, we don't
- * use the error field here, so no need to update sBIT.
+ * use the error field here, so no need to update sBIT. in_pixel
+ * says whether we expect libpng to change the output format.
*/
- image_pixel_init(&out_pixel, pRow, out_ct, out_bd, x, out_palette);
+ image_pixel_init(&out_pixel, pRow, out_ct, out_bd, x, out_palette,
+ &in_pixel);
/* We don't expect changes to the index here even if the bit depth is
* changed.
/* A single test run. */
static void
-transform_test(png_modifier *pmIn, PNG_CONST png_uint_32 idIn,
- PNG_CONST image_transform* transform_listIn, PNG_CONST char * volatile name)
+transform_test(png_modifier *pmIn, png_uint_32 idIn,
+ const image_transform* transform_listIn, const char * const name)
{
transform_display d;
context(&pmIn->this, fault);
#define PT ITSTRUCT(end) /* stores the previous transform */
/* To save code: */
-static void
-image_transform_default_ini(PNG_CONST image_transform *this,
+extern void image_transform_default_ini(const image_transform *this,
+ transform_display *that); /* silence GCC warnings */
+
+void /* private, but almost always needed */
+image_transform_default_ini(const image_transform *this,
transform_display *that)
{
this->next->ini(this->next, that);
#ifdef PNG_READ_BACKGROUND_SUPPORTED
static int
image_transform_default_add(image_transform *this,
- PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
{
UNUSED(colour_type)
UNUSED(bit_depth)
#ifdef PNG_READ_EXPAND_SUPPORTED
/* png_set_palette_to_rgb */
static void
-image_transform_png_set_palette_to_rgb_set(PNG_CONST image_transform *this,
+image_transform_png_set_palette_to_rgb_set(const image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_palette_to_rgb(pp);
}
static void
-image_transform_png_set_palette_to_rgb_mod(PNG_CONST image_transform *this,
+image_transform_png_set_palette_to_rgb_mod(const image_transform *this,
image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
+ const transform_display *display)
{
if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
image_pixel_convert_PLTE(that);
static int
image_transform_png_set_palette_to_rgb_add(image_transform *this,
- PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
{
UNUSED(bit_depth)
#ifdef PNG_READ_EXPAND_SUPPORTED
/* png_set_tRNS_to_alpha */
static void
-image_transform_png_set_tRNS_to_alpha_set(PNG_CONST image_transform *this,
+image_transform_png_set_tRNS_to_alpha_set(const image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_tRNS_to_alpha(pp);
+
+ /* If there was a tRNS chunk that would get expanded and add an alpha
+ * channel is_transparent must be updated:
+ */
+ if (that->this.has_tRNS)
+ that->this.is_transparent = 1;
+
this->next->set(this->next, that, pp, pi);
}
static void
-image_transform_png_set_tRNS_to_alpha_mod(PNG_CONST image_transform *this,
+image_transform_png_set_tRNS_to_alpha_mod(const image_transform *this,
image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
+ const transform_display *display)
{
+#if PNG_LIBPNG_VER < 10700
/* LIBPNG BUG: this always forces palette images to RGB. */
if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
image_pixel_convert_PLTE(that);
+#endif
/* This effectively does an 'expand' only if there is some transparency to
* convert to an alpha channel.
*/
if (that->have_tRNS)
- image_pixel_add_alpha(that, &display->this);
+# if PNG_LIBPNG_VER >= 10700
+ if (that->colour_type != PNG_COLOR_TYPE_PALETTE &&
+ (that->colour_type & PNG_COLOR_MASK_ALPHA) == 0)
+# endif
+ image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
+#if PNG_LIBPNG_VER < 10700
/* LIBPNG BUG: otherwise libpng still expands to 8 bits! */
else
{
if (that->sample_depth < 8)
that->sample_depth = 8;
}
+#endif
this->next->mod(this->next, that, pp, display);
}
static int
image_transform_png_set_tRNS_to_alpha_add(image_transform *this,
- PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
{
UNUSED(bit_depth)
/* We don't know yet whether there will be a tRNS chunk, but we know that
* this transformation should do nothing if there already is an alpha
- * channel.
+ * channel. In addition, after the bug fix in 1.7.0, there is no longer
+ * any action on a palette image.
*/
- return (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
+ return
+# if PNG_LIBPNG_VER >= 10700
+ colour_type != PNG_COLOR_TYPE_PALETTE &&
+# endif
+ (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
}
IT(tRNS_to_alpha);
#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
/* png_set_gray_to_rgb */
static void
-image_transform_png_set_gray_to_rgb_set(PNG_CONST image_transform *this,
+image_transform_png_set_gray_to_rgb_set(const image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_gray_to_rgb(pp);
+ /* NOTE: this doesn't result in tRNS expansion. */
this->next->set(this->next, that, pp, pi);
}
static void
-image_transform_png_set_gray_to_rgb_mod(PNG_CONST image_transform *this,
+image_transform_png_set_gray_to_rgb_mod(const image_transform *this,
image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
+ const transform_display *display)
{
/* NOTE: we can actually pend the tRNS processing at this point because we
* can correctly recognize the original pixel value even though we have
* doesn't do this, so we don't either.
*/
if ((that->colour_type & PNG_COLOR_MASK_COLOR) == 0 && that->have_tRNS)
- image_pixel_add_alpha(that, &display->this);
+ image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
/* Simply expand the bit depth and alter the colour type as required. */
if (that->colour_type == PNG_COLOR_TYPE_GRAY)
static int
image_transform_png_set_gray_to_rgb_add(image_transform *this,
- PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
{
UNUSED(bit_depth)
#ifdef PNG_READ_EXPAND_SUPPORTED
/* png_set_expand */
static void
-image_transform_png_set_expand_set(PNG_CONST image_transform *this,
+image_transform_png_set_expand_set(const image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_expand(pp);
+
+ if (that->this.has_tRNS)
+ that->this.is_transparent = 1;
+
this->next->set(this->next, that, pp, pi);
}
static void
-image_transform_png_set_expand_mod(PNG_CONST image_transform *this,
+image_transform_png_set_expand_mod(const image_transform *this,
image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
+ const transform_display *display)
{
/* The general expand case depends on what the colour type is: */
if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
that->sample_depth = that->bit_depth = 8;
if (that->have_tRNS)
- image_pixel_add_alpha(that, &display->this);
+ image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
this->next->mod(this->next, that, pp, display);
}
static int
image_transform_png_set_expand_add(image_transform *this,
- PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
{
UNUSED(bit_depth)
#ifdef PNG_READ_EXPAND_SUPPORTED
/* png_set_expand_gray_1_2_4_to_8
- * LIBPNG BUG: this just does an 'expand'
+ * Pre 1.7.0 LIBPNG BUG: this just does an 'expand'
*/
static void
image_transform_png_set_expand_gray_1_2_4_to_8_set(
- PNG_CONST image_transform *this, transform_display *that, png_structp pp,
+ const image_transform *this, transform_display *that, png_structp pp,
png_infop pi)
{
png_set_expand_gray_1_2_4_to_8(pp);
+ /* NOTE: don't expect this to expand tRNS */
this->next->set(this->next, that, pp, pi);
}
static void
image_transform_png_set_expand_gray_1_2_4_to_8_mod(
- PNG_CONST image_transform *this, image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
+ const image_transform *this, image_pixel *that, png_const_structp pp,
+ const transform_display *display)
{
+#if PNG_LIBPNG_VER < 10700
image_transform_png_set_expand_mod(this, that, pp, display);
+#else
+ /* Only expand grayscale of bit depth less than 8: */
+ if (that->colour_type == PNG_COLOR_TYPE_GRAY &&
+ that->bit_depth < 8)
+ that->sample_depth = that->bit_depth = 8;
+
+ this->next->mod(this->next, that, pp, display);
+#endif /* 1.7 or later */
}
static int
image_transform_png_set_expand_gray_1_2_4_to_8_add(image_transform *this,
- PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
{
+#if PNG_LIBPNG_VER < 10700
return image_transform_png_set_expand_add(this, that, colour_type,
bit_depth);
+#else
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ /* This should do nothing unless the color type is gray and the bit depth is
+ * less than 8:
+ */
+ return colour_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8;
+#endif /* 1.7 or later */
}
IT(expand_gray_1_2_4_to_8);
#ifdef PNG_READ_EXPAND_16_SUPPORTED
/* png_set_expand_16 */
static void
-image_transform_png_set_expand_16_set(PNG_CONST image_transform *this,
+image_transform_png_set_expand_16_set(const image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_expand_16(pp);
+
+ /* NOTE: prior to 1.7 libpng does SET_EXPAND as well, so tRNS is expanded. */
+# if PNG_LIBPNG_VER < 10700
+ if (that->this.has_tRNS)
+ that->this.is_transparent = 1;
+# endif
+
this->next->set(this->next, that, pp, pi);
}
static void
-image_transform_png_set_expand_16_mod(PNG_CONST image_transform *this,
+image_transform_png_set_expand_16_mod(const image_transform *this,
image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
+ const transform_display *display)
{
/* Expect expand_16 to expand everything to 16 bits as a result of also
* causing 'expand' to happen.
image_pixel_convert_PLTE(that);
if (that->have_tRNS)
- image_pixel_add_alpha(that, &display->this);
+ image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
if (that->bit_depth < 16)
that->sample_depth = that->bit_depth = 16;
static int
image_transform_png_set_expand_16_add(image_transform *this,
- PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
{
UNUSED(colour_type)
#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED /* API added in 1.5.4 */
/* png_set_scale_16 */
static void
-image_transform_png_set_scale_16_set(PNG_CONST image_transform *this,
+image_transform_png_set_scale_16_set(const image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_scale_16(pp);
+# if PNG_LIBPNG_VER < 10700
+ /* libpng will limit the gamma table size: */
+ that->max_gamma_8 = PNG_MAX_GAMMA_8;
+# endif
this->next->set(this->next, that, pp, pi);
}
static void
-image_transform_png_set_scale_16_mod(PNG_CONST image_transform *this,
+image_transform_png_set_scale_16_mod(const image_transform *this,
image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
+ const transform_display *display)
{
if (that->bit_depth == 16)
{
static int
image_transform_png_set_scale_16_add(image_transform *this,
- PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
{
UNUSED(colour_type)
#ifdef PNG_READ_16_TO_8_SUPPORTED /* the default before 1.5.4 */
/* png_set_strip_16 */
static void
-image_transform_png_set_strip_16_set(PNG_CONST image_transform *this,
+image_transform_png_set_strip_16_set(const image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_strip_16(pp);
+# if PNG_LIBPNG_VER < 10700
+ /* libpng will limit the gamma table size: */
+ that->max_gamma_8 = PNG_MAX_GAMMA_8;
+# endif
this->next->set(this->next, that, pp, pi);
}
static void
-image_transform_png_set_strip_16_mod(PNG_CONST image_transform *this,
+image_transform_png_set_strip_16_mod(const image_transform *this,
image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
+ const transform_display *display)
{
if (that->bit_depth == 16)
{
* png_set_scale_16 API in 1.5.4 (but 1.5.4+ always defines the above!)
*/
{
- PNG_CONST double d = (255-128.5)/65535;
+ const double d = (255-128.5)/65535;
that->rede += d;
that->greene += d;
that->bluee += d;
static int
image_transform_png_set_strip_16_add(image_transform *this,
- PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
{
UNUSED(colour_type)
#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
/* png_set_strip_alpha */
static void
-image_transform_png_set_strip_alpha_set(PNG_CONST image_transform *this,
+image_transform_png_set_strip_alpha_set(const image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_strip_alpha(pp);
}
static void
-image_transform_png_set_strip_alpha_mod(PNG_CONST image_transform *this,
+image_transform_png_set_strip_alpha_mod(const image_transform *this,
image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
+ const transform_display *display)
{
if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
that->colour_type = PNG_COLOR_TYPE_GRAY;
static int
image_transform_png_set_strip_alpha_add(image_transform *this,
- PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
{
UNUSED(bit_depth)
* png_fixed_point green)
* png_get_rgb_to_gray_status
*
- * The 'default' test here uses values known to be used inside libpng:
+ * The 'default' test here uses values known to be used inside libpng prior to
+ * 1.7.0:
*
* red: 6968
* green: 23434
#undef image_transform_ini
#define image_transform_ini image_transform_png_set_rgb_to_gray_ini
static void
-image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this,
+image_transform_png_set_rgb_to_gray_ini(const image_transform *this,
transform_display *that)
{
png_modifier *pm = that->pm;
- PNG_CONST color_encoding *e = pm->current_encoding;
+ const color_encoding *e = pm->current_encoding;
UNUSED(this)
/* Coefficients come from the encoding, but may need to be normalized to a
* white point Y of 1.0
*/
- PNG_CONST double whiteY = e->red.Y + e->green.Y + e->blue.Y;
+ const double whiteY = e->red.Y + e->green.Y + e->blue.Y;
data.red_coefficient = e->red.Y;
data.green_coefficient = e->green.Y;
else
{
- /* The default (built in) coeffcients, as above: */
- data.red_coefficient = 6968 / 32768.;
- data.green_coefficient = 23434 / 32768.;
- data.blue_coefficient = 2366 / 32768.;
+ /* The default (built in) coefficients, as above: */
+# if PNG_LIBPNG_VER < 10700
+ data.red_coefficient = 6968 / 32768.;
+ data.green_coefficient = 23434 / 32768.;
+ data.blue_coefficient = 2366 / 32768.;
+# else
+ data.red_coefficient = .2126;
+ data.green_coefficient = .7152;
+ data.blue_coefficient = .0722;
+# endif
}
data.gamma = pm->current_gamma;
png_uint_32 ru;
double total;
- RANDOMIZE(ru);
+ ru = random_u32();
data.green_coefficient = total = (ru & 0xffff) / 65535.;
ru >>= 16;
data.red_coefficient = (1 - total) * (ru & 0xffff) / 65535.;
* conversion adds another +/-2 in the 16-bit case and
* +/-(1<<(15-PNG_MAX_GAMMA_8)) in the 8-bit case.
*/
- that->pm->limit +=
-# if PNG_MAX_GAMMA_8 < 14
- pow((that->this.bit_depth == 16 ?
- 8. : 6. + (1<<(15-PNG_MAX_GAMMA_8)))/65535, data.gamma);
-# else
- pow((that->this.bit_depth == 16 ?
- 8. : 8. + (1<<(15-PNG_MAX_GAMMA_8)))/65535, data.gamma);
+# if PNG_LIBPNG_VER < 10700
+ if (that->this.bit_depth < 16)
+ that->max_gamma_8 = PNG_MAX_GAMMA_8;
# endif
+ that->pm->limit += pow(
+ (that->this.bit_depth == 16 || that->max_gamma_8 > 14 ?
+ 8. :
+ 6. + (1<<(15-that->max_gamma_8))
+ )/65535, data.gamma);
}
else
* When DIGITIZE is set because a pre-1.7 version of libpng is being
* tested allow a bigger slack.
*
- * NOTE: this magic number was determined by experiment to be 1.1 (when
- * using fixed point arithmetic). There's no great merit to the value
- * below, however it only affects the limit used for checking for
- * internal calculation errors, not the actual limit imposed by
- * pngvalid on the output errors.
+ * NOTE: this number only affects the internal limit check in pngvalid,
+ * it has no effect on the limits applied to the libpng values.
*/
- that->pm->limit +=
-# if DIGITIZE
- pow(1.1 /255, data.gamma);
-# else
- pow(1.0 /255, data.gamma);
-# endif
+#if DIGITIZE
+ that->pm->limit += pow( 2.0/255, data.gamma);
+#else
+ that->pm->limit += pow( 1.0/255, data.gamma);
+#endif
}
}
}
static void
-image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this,
+image_transform_png_set_rgb_to_gray_set(const image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
- PNG_CONST int error_action = 1; /* no error, no defines in png.h */
+ int error_action = 1; /* no error, no defines in png.h */
# ifdef PNG_FLOATING_POINT_SUPPORTED
png_set_rgb_to_gray(pp, error_action, data.red_to_set, data.green_to_set);
& PNG_INFO_cHRM) != 0)
{
double maxe;
- PNG_CONST char *el;
+ const char *el;
color_encoding e, o;
/* Expect libpng to return a normalized result, but the original
}
static void
-image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
+image_transform_png_set_rgb_to_gray_mod(const image_transform *this,
image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
+ const transform_display *display)
{
if ((that->colour_type & PNG_COLOR_MASK_COLOR) != 0)
{
double gray, err;
- if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
- image_pixel_convert_PLTE(that);
+# if PNG_LIBPNG_VER < 10700
+ if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+ image_pixel_convert_PLTE(that);
+# endif
/* Image now has RGB channels... */
# if DIGITIZE
{
- PNG_CONST png_modifier *pm = display->pm;
- const unsigned int sample_depth = that->sample_depth;
- const unsigned int calc_depth = (pm->assume_16_bit_calculations ? 16 :
+ png_modifier *pm = display->pm;
+ unsigned int sample_depth = that->sample_depth;
+ unsigned int calc_depth = (pm->assume_16_bit_calculations ? 16 :
sample_depth);
- const unsigned int gamma_depth = (sample_depth == 16 ? 16 :
- (pm->assume_16_bit_calculations ? PNG_MAX_GAMMA_8 : sample_depth));
+ unsigned int gamma_depth =
+ (sample_depth == 16 ?
+ display->max_gamma_8 :
+ (pm->assume_16_bit_calculations ?
+ display->max_gamma_8 :
+ sample_depth));
int isgray;
double r, g, b;
double rlo, rhi, glo, ghi, blo, bhi, graylo, grayhi;
* will be identical after this operation if there is only one
* transform, feel free to delete the png_error checks on this below in
* the future (this is just me trying to ensure it works!)
+ *
+ * Interval arithmetic is exact, but to implement it it must be
+ * possible to control the floating point implementation rounding mode.
+ * This cannot be done in ANSI-C, so instead I reduce the 'lo' values
+ * by DBL_EPSILON and increase the 'hi' values by the same.
*/
+# define DD(v,d,r) (digitize(v*(1-DBL_EPSILON), d, r) * (1-DBL_EPSILON))
+# define DU(v,d,r) (digitize(v*(1+DBL_EPSILON), d, r) * (1+DBL_EPSILON))
+
r = rlo = rhi = that->redf;
rlo -= that->rede;
- rlo = digitize(rlo, calc_depth, 1/*round*/);
+ rlo = DD(rlo, calc_depth, 1/*round*/);
rhi += that->rede;
- rhi = digitize(rhi, calc_depth, 1/*round*/);
+ rhi = DU(rhi, calc_depth, 1/*round*/);
g = glo = ghi = that->greenf;
glo -= that->greene;
- glo = digitize(glo, calc_depth, 1/*round*/);
+ glo = DD(glo, calc_depth, 1/*round*/);
ghi += that->greene;
- ghi = digitize(ghi, calc_depth, 1/*round*/);
+ ghi = DU(ghi, calc_depth, 1/*round*/);
b = blo = bhi = that->bluef;
blo -= that->bluee;
- blo = digitize(blo, calc_depth, 1/*round*/);
- bhi += that->greene;
- bhi = digitize(bhi, calc_depth, 1/*round*/);
+ blo = DD(blo, calc_depth, 1/*round*/);
+ bhi += that->bluee;
+ bhi = DU(bhi, calc_depth, 1/*round*/);
isgray = r==g && g==b;
if (data.gamma != 1)
{
- PNG_CONST double power = 1/data.gamma;
- PNG_CONST double abse = calc_depth == 16 ? .5/65535 : .5/255;
-
- /* 'abse' is the absolute error permitted in linear calculations. It
- * is used here to capture the error permitted in the handling
- * (undoing) of the gamma encoding. Once again digitization occurs
- * to handle the upper and lower bounds of the values. This is
- * where the real errors are introduced.
+ const double power = 1/data.gamma;
+ const double abse = .5/(sample_depth == 16 ? 65535 : 255);
+
+ /* If a gamma calculation is done it is done using lookup tables of
+ * precision gamma_depth, so the already digitized value above may
+ * need to be further digitized here.
*/
+ if (gamma_depth != calc_depth)
+ {
+ rlo = DD(rlo, gamma_depth, 0/*truncate*/);
+ rhi = DU(rhi, gamma_depth, 0/*truncate*/);
+ glo = DD(glo, gamma_depth, 0/*truncate*/);
+ ghi = DU(ghi, gamma_depth, 0/*truncate*/);
+ blo = DD(blo, gamma_depth, 0/*truncate*/);
+ bhi = DU(bhi, gamma_depth, 0/*truncate*/);
+ }
+
+ /* 'abse' is the error in the gamma table calculation itself. */
r = pow(r, power);
- rlo = digitize(pow(rlo, power)-abse, calc_depth, 1);
- rhi = digitize(pow(rhi, power)+abse, calc_depth, 1);
+ rlo = DD(pow(rlo, power)-abse, calc_depth, 1);
+ rhi = DU(pow(rhi, power)+abse, calc_depth, 1);
g = pow(g, power);
- glo = digitize(pow(glo, power)-abse, calc_depth, 1);
- ghi = digitize(pow(ghi, power)+abse, calc_depth, 1);
+ glo = DD(pow(glo, power)-abse, calc_depth, 1);
+ ghi = DU(pow(ghi, power)+abse, calc_depth, 1);
b = pow(b, power);
- blo = digitize(pow(blo, power)-abse, calc_depth, 1);
- bhi = digitize(pow(bhi, power)+abse, calc_depth, 1);
+ blo = DD(pow(blo, power)-abse, calc_depth, 1);
+ bhi = DU(pow(bhi, power)+abse, calc_depth, 1);
}
/* Now calculate the actual gray values. Although the error in the
* coefficients depends on whether they were specified on the command
* line (in which case truncation to 15 bits happened) or not (rounding
- * was used) the maxium error in an individual coefficient is always
- * 1/32768, because even in the rounding case the requirement that
+ * was used) the maximum error in an individual coefficient is always
+ * 2/32768, because even in the rounding case the requirement that
* coefficients add up to 32768 can cause a larger rounding error.
*
* The only time when rounding doesn't occur in 1.5.5 and later is when
b * data.blue_coefficient;
{
- PNG_CONST int do_round = data.gamma != 1 || calc_depth == 16;
- PNG_CONST double ce = 1. / 32768;
+ int do_round = data.gamma != 1 || calc_depth == 16;
+ const double ce = 2. / 32768;
- graylo = digitize(rlo * (data.red_coefficient-ce) +
+ graylo = DD(rlo * (data.red_coefficient-ce) +
glo * (data.green_coefficient-ce) +
- blo * (data.blue_coefficient-ce), gamma_depth, do_round);
- if (graylo <= 0)
- graylo = 0;
+ blo * (data.blue_coefficient-ce), calc_depth, do_round);
+ if (graylo > gray) /* always accept the right answer */
+ graylo = gray;
- grayhi = digitize(rhi * (data.red_coefficient+ce) +
+ grayhi = DU(rhi * (data.red_coefficient+ce) +
ghi * (data.green_coefficient+ce) +
- bhi * (data.blue_coefficient+ce), gamma_depth, do_round);
- if (grayhi >= 1)
- grayhi = 1;
+ bhi * (data.blue_coefficient+ce), calc_depth, do_round);
+ if (grayhi < gray)
+ grayhi = gray;
}
/* And invert the gamma. */
if (data.gamma != 1)
{
- PNG_CONST double power = data.gamma;
+ const double power = data.gamma;
+
+ /* And this happens yet again, shifting the values once more. */
+ if (gamma_depth != sample_depth)
+ {
+ rlo = DD(rlo, gamma_depth, 0/*truncate*/);
+ rhi = DU(rhi, gamma_depth, 0/*truncate*/);
+ glo = DD(glo, gamma_depth, 0/*truncate*/);
+ ghi = DU(ghi, gamma_depth, 0/*truncate*/);
+ blo = DD(blo, gamma_depth, 0/*truncate*/);
+ bhi = DU(bhi, gamma_depth, 0/*truncate*/);
+ }
gray = pow(gray, power);
- graylo = digitize(pow(graylo, power), sample_depth, 1);
- grayhi = digitize(pow(grayhi, power), sample_depth, 1);
+ graylo = DD(pow(graylo, power), sample_depth, 1);
+ grayhi = DU(pow(grayhi, power), sample_depth, 1);
}
+# undef DD
+# undef DU
+
/* Now the error can be calculated.
*
* If r==g==b because there is no overall gamma correction libpng
else
{
err = fabs(grayhi-gray);
+
if (fabs(gray - graylo) > err)
err = fabs(graylo-gray);
+#if !RELEASE_BUILD
/* Check that this worked: */
if (err > pm->limit)
{
pos = safecatd(buffer, sizeof buffer, pos, err, 6);
pos = safecat(buffer, sizeof buffer, pos, " exceeds limit ");
pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6);
- png_error(pp, buffer);
+ png_warning(pp, buffer);
+ pm->limit = err;
}
+#endif /* !RELEASE_BUILD */
}
}
-# else /* DIGITIZE */
+# else /* !DIGITIZE */
{
double r = that->redf;
double re = that->rede;
double b = that->bluef;
double be = that->bluee;
- /* The true gray case involves no math. */
- if (r == g && r == b)
- {
- gray = r;
- err = re;
- if (err < ge) err = ge;
- if (err < be) err = be;
- }
+# if PNG_LIBPNG_VER < 10700
+ /* The true gray case involves no math in earlier versions (not
+ * true, there was some if gamma correction was happening too.)
+ */
+ if (r == g && r == b)
+ {
+ gray = r;
+ err = re;
+ if (err < ge) err = ge;
+ if (err < be) err = be;
+ }
- else if (data.gamma == 1)
+ else
+# endif /* before 1.7 */
+ if (data.gamma == 1)
{
/* There is no need to do the conversions to and from linear space,
* so the calculation should be a lot more accurate. There is a
- * built in 1/32768 error in the coefficients because they only have
- * 15 bits and are adjusted to make sure they add up to 32768, so
- * the result may have an additional error up to 1/32768. (Note
- * that adding the 1/32768 here avoids needing to increase the
- * global error limits to take this into account.)
+ * built in error in the coefficients because they only have 15 bits
+ * and are adjusted to make sure they add up to 32768. This
+ * involves a integer calculation with truncation of the form:
+ *
+ * ((int)(coefficient * 100000) * 32768)/100000
+ *
+ * This is done to the red and green coefficients (the ones
+ * provided to the API) then blue is calculated from them so the
+ * result adds up to 32768. In the worst case this can result in
+ * a -1 error in red and green and a +2 error in blue. Consequently
+ * the worst case in the calculation below is 2/32768 error.
+ *
+ * TODO: consider fixing this in libpng by rounding the calculation
+ * limiting the error to 1/32768.
+ *
+ * Handling this by adding 2/32768 here avoids needing to increase
+ * the global error limits to take this into account.)
*/
gray = r * data.red_coefficient + g * data.green_coefficient +
b * data.blue_coefficient;
err = re * data.red_coefficient + ge * data.green_coefficient +
- be * data.blue_coefficient + 1./32768 + gray * 5 * DBL_EPSILON;
+ be * data.blue_coefficient + 2./32768 + gray * 5 * DBL_EPSILON;
}
else
* lookups in the calculation and each introduces a quantization
* error defined by the table size.
*/
- PNG_CONST png_modifier *pm = display->pm;
+ png_modifier *pm = display->pm;
double in_qe = (that->sample_depth > 8 ? .5/65535 : .5/255);
double out_qe = (that->sample_depth > 8 ? .5/65535 :
- (pm->assume_16_bit_calculations ? .5/(1<<PNG_MAX_GAMMA_8) :
+ (pm->assume_16_bit_calculations ? .5/(1<<display->max_gamma_8) :
.5/255));
double rhi, ghi, bhi, grayhi;
double g1 = 1/data.gamma;
* previously added input quantization error at this point.
*/
gray = r * data.red_coefficient + g * data.green_coefficient +
- b * data.blue_coefficient - 1./32768 - out_qe;
+ b * data.blue_coefficient - 2./32768 - out_qe;
if (gray <= 0)
gray = 0;
else
}
grayhi = rhi * data.red_coefficient + ghi * data.green_coefficient +
- bhi * data.blue_coefficient + 1./32768 + out_qe;
+ bhi * data.blue_coefficient + 2./32768 + out_qe;
grayhi *= (1 + 6 * DBL_EPSILON);
if (grayhi >= 1)
grayhi = 1;
else
err -= in_qe;
+#if !RELEASE_BUILD
/* Validate that the error is within limits (this has caused
* problems before, it's much easier to detect them here.)
*/
pos = safecatd(buffer, sizeof buffer, pos, err, 6);
pos = safecat(buffer, sizeof buffer, pos, " exceeds limit ");
pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6);
- png_error(pp, buffer);
+ png_warning(pp, buffer);
+ pm->limit = err;
}
+#endif /* !RELEASE_BUILD */
}
}
# endif /* !DIGITIZE */
that->bluef = that->greenf = that->redf = gray;
that->bluee = that->greene = that->rede = err;
- /* The sBIT is the minium of the three colour channel sBITs. */
+ /* The sBIT is the minimum of the three colour channel sBITs. */
if (that->red_sBIT > that->green_sBIT)
that->red_sBIT = that->green_sBIT;
if (that->red_sBIT > that->blue_sBIT)
static int
image_transform_png_set_rgb_to_gray_add(image_transform *this,
- PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
{
UNUSED(bit_depth)
static image_pixel data;
static void
-image_transform_png_set_background_set(PNG_CONST image_transform *this,
+image_transform_png_set_background_set(const image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_byte colour_type, bit_depth;
* so we need to know what that is! The background colour is stored in the
* transform_display.
*/
- RANDOMIZE(random_bytes);
+ R8(random_bytes);
/* Read the random value, for colour type 3 the background colour is actually
* expressed as a 24bit rgb, not an index.
else
{
+ if (that->this.has_tRNS)
+ that->this.is_transparent = 1;
+
bit_depth = that->this.bit_depth;
expand = 1;
}
image_pixel_init(&data, random_bytes, colour_type,
- bit_depth, 0/*x*/, 0/*unused: palette*/);
+ bit_depth, 0/*x*/, 0/*unused: palette*/, NULL/*format*/);
/* Extract the background colour from this image_pixel, but make sure the
* unused fields of 'back' are garbage.
*/
- RANDOMIZE(back);
+ R8(back);
if (colour_type & PNG_COLOR_MASK_COLOR)
{
else
back.gray = (png_uint_16)data.red;
-# ifdef PNG_FLOATING_POINT_SUPPORTED
- png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0);
-# else
- png_set_background_fixed(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0);
-# endif
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+ png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0);
+#else
+ png_set_background_fixed(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0);
+#endif
+
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_background_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ /* Check for tRNS first: */
+ if (that->have_tRNS && that->colour_type != PNG_COLOR_TYPE_PALETTE)
+ image_pixel_add_alpha(that, &display->this, 1/*for background*/);
+
+ /* This is only necessary if the alpha value is less than 1. */
+ if (that->alphaf < 1)
+ {
+ /* Now we do the background calculation without any gamma correction. */
+ if (that->alphaf <= 0)
+ {
+ that->redf = data.redf;
+ that->greenf = data.greenf;
+ that->bluef = data.bluef;
+
+ that->rede = data.rede;
+ that->greene = data.greene;
+ that->bluee = data.bluee;
+
+ that->red_sBIT= data.red_sBIT;
+ that->green_sBIT= data.green_sBIT;
+ that->blue_sBIT= data.blue_sBIT;
+ }
+
+ else /* 0 < alpha < 1 */
+ {
+ double alf = 1 - that->alphaf;
+
+ that->redf = that->redf * that->alphaf + data.redf * alf;
+ that->rede = that->rede * that->alphaf + data.rede * alf +
+ DBL_EPSILON;
+ that->greenf = that->greenf * that->alphaf + data.greenf * alf;
+ that->greene = that->greene * that->alphaf + data.greene * alf +
+ DBL_EPSILON;
+ that->bluef = that->bluef * that->alphaf + data.bluef * alf;
+ that->bluee = that->bluee * that->alphaf + data.bluee * alf +
+ DBL_EPSILON;
+ }
+
+ /* Remove the alpha type and set the alpha (not in that order.) */
+ that->alphaf = 1;
+ that->alphae = 0;
+ }
+
+ if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ that->colour_type = PNG_COLOR_TYPE_RGB;
+ else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ that->colour_type = PNG_COLOR_TYPE_GRAY;
+ /* PNG_COLOR_TYPE_PALETTE is not changed */
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+#define image_transform_png_set_background_add image_transform_default_add
+
+#undef data
+IT(background);
+#undef PT
+#define PT ITSTRUCT(background)
+#endif /* PNG_READ_BACKGROUND_SUPPORTED */
+
+/* png_set_quantize(png_structp, png_colorp palette, int num_palette,
+ * int maximum_colors, png_const_uint_16p histogram, int full_quantize)
+ *
+ * Very difficult to validate this!
+ */
+/*NOTE: TBD NYI */
+
+/* The data layout transforms are handled by swapping our own channel data,
+ * necessarily these need to happen at the end of the transform list because the
+ * semantic of the channels changes after these are executed. Some of these,
+ * like set_shift and set_packing, can't be done at present because they change
+ * the layout of the data at the sub-sample level so sample() won't get the
+ * right answer.
+ */
+/* png_set_invert_alpha */
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+/* Invert the alpha channel
+ *
+ * png_set_invert_alpha(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_invert_alpha_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_invert_alpha(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_invert_alpha_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->colour_type & 4)
+ that->alpha_inverted = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_invert_alpha_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ /* Only has an effect on pixels with alpha: */
+ return (colour_type & 4) != 0;
+}
+
+IT(invert_alpha);
+#undef PT
+#define PT ITSTRUCT(invert_alpha)
+
+#endif /* PNG_READ_INVERT_ALPHA_SUPPORTED */
+
+/* png_set_bgr */
+#ifdef PNG_READ_BGR_SUPPORTED
+/* Swap R,G,B channels to order B,G,R.
+ *
+ * png_set_bgr(png_structrp png_ptr)
+ *
+ * This only has an effect on RGB and RGBA pixels.
+ */
+static void
+image_transform_png_set_bgr_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_bgr(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_bgr_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->colour_type == PNG_COLOR_TYPE_RGB ||
+ that->colour_type == PNG_COLOR_TYPE_RGBA)
+ that->swap_rgb = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_bgr_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ return colour_type == PNG_COLOR_TYPE_RGB ||
+ colour_type == PNG_COLOR_TYPE_RGBA;
+}
+
+IT(bgr);
+#undef PT
+#define PT ITSTRUCT(bgr)
+
+#endif /* PNG_READ_BGR_SUPPORTED */
+
+/* png_set_swap_alpha */
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+/* Put the alpha channel first.
+ *
+ * png_set_swap_alpha(png_structrp png_ptr)
+ *
+ * This only has an effect on GA and RGBA pixels.
+ */
+static void
+image_transform_png_set_swap_alpha_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_swap_alpha(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_swap_alpha_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->colour_type == PNG_COLOR_TYPE_GA ||
+ that->colour_type == PNG_COLOR_TYPE_RGBA)
+ that->alpha_first = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_swap_alpha_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ return colour_type == PNG_COLOR_TYPE_GA ||
+ colour_type == PNG_COLOR_TYPE_RGBA;
+}
+
+IT(swap_alpha);
+#undef PT
+#define PT ITSTRUCT(swap_alpha)
+
+#endif /* PNG_READ_SWAP_ALPHA_SUPPORTED */
+
+/* png_set_swap */
+#ifdef PNG_READ_SWAP_SUPPORTED
+/* Byte swap 16-bit components.
+ *
+ * png_set_swap(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_swap_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_swap(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_swap_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->bit_depth == 16)
+ that->swap16 = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_swap_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(colour_type)
+
+ this->next = *that;
+ *that = this;
+
+ return bit_depth == 16;
+}
+
+IT(swap);
+#undef PT
+#define PT ITSTRUCT(swap)
+
+#endif /* PNG_READ_SWAP_SUPPORTED */
+
+#ifdef PNG_READ_FILLER_SUPPORTED
+/* Add a filler byte to 8-bit Gray or 24-bit RGB images.
+ *
+ * png_set_filler, (png_structp png_ptr, png_uint_32 filler, int flags));
+ *
+ * Flags:
+ *
+ * PNG_FILLER_BEFORE
+ * PNG_FILLER_AFTER
+ */
+#define data ITDATA(filler)
+static struct
+{
+ png_uint_32 filler;
+ int flags;
+} data;
+
+static void
+image_transform_png_set_filler_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ /* Need a random choice for 'before' and 'after' as well as for the
+ * filler. The 'filler' value has all 32 bits set, but only bit_depth
+ * will be used. At this point we don't know bit_depth.
+ */
+ data.filler = random_u32();
+ data.flags = random_choice();
+
+ png_set_filler(pp, data.filler, data.flags);
+
+ /* The standard display handling stuff also needs to know that
+ * there is a filler, so set that here.
+ */
+ that->this.filler = 1;
+
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_filler_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->bit_depth >= 8 &&
+ (that->colour_type == PNG_COLOR_TYPE_RGB ||
+ that->colour_type == PNG_COLOR_TYPE_GRAY))
+ {
+ unsigned int max = (1U << that->bit_depth)-1;
+ that->alpha = data.filler & max;
+ that->alphaf = ((double)that->alpha) / max;
+ that->alphae = 0;
+
+ /* The filler has been stored in the alpha channel, we must record
+ * that this has been done for the checking later on, the color
+ * type is faked to have an alpha channel, but libpng won't report
+ * this; the app has to know the extra channel is there and this
+ * was recording in standard_display::filler above.
+ */
+ that->colour_type |= 4; /* alpha added */
+ that->alpha_first = data.flags == PNG_FILLER_BEFORE;
+ }
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_filler_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ this->next = *that;
+ *that = this;
+
+ return bit_depth >= 8 && (colour_type == PNG_COLOR_TYPE_RGB ||
+ colour_type == PNG_COLOR_TYPE_GRAY);
+}
+
+#undef data
+IT(filler);
+#undef PT
+#define PT ITSTRUCT(filler)
+
+/* png_set_add_alpha, (png_structp png_ptr, png_uint_32 filler, int flags)); */
+/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */
+#define data ITDATA(add_alpha)
+static struct
+{
+ png_uint_32 filler;
+ int flags;
+} data;
+
+static void
+image_transform_png_set_add_alpha_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ /* Need a random choice for 'before' and 'after' as well as for the
+ * filler. The 'filler' value has all 32 bits set, but only bit_depth
+ * will be used. At this point we don't know bit_depth.
+ */
+ data.filler = random_u32();
+ data.flags = random_choice();
+
+ png_set_add_alpha(pp, data.filler, data.flags);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_add_alpha_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->bit_depth >= 8 &&
+ (that->colour_type == PNG_COLOR_TYPE_RGB ||
+ that->colour_type == PNG_COLOR_TYPE_GRAY))
+ {
+ unsigned int max = (1U << that->bit_depth)-1;
+ that->alpha = data.filler & max;
+ that->alphaf = ((double)that->alpha) / max;
+ that->alphae = 0;
+
+ that->colour_type |= 4; /* alpha added */
+ that->alpha_first = data.flags == PNG_FILLER_BEFORE;
+ }
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_add_alpha_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ this->next = *that;
+ *that = this;
+
+ return bit_depth >= 8 && (colour_type == PNG_COLOR_TYPE_RGB ||
+ colour_type == PNG_COLOR_TYPE_GRAY);
+}
+
+#undef data
+IT(add_alpha);
+#undef PT
+#define PT ITSTRUCT(add_alpha)
+
+#endif /* PNG_READ_FILLER_SUPPORTED */
+
+/* png_set_packing */
+#ifdef PNG_READ_PACK_SUPPORTED
+/* Use 1 byte per pixel in 1, 2, or 4-bit depth files.
+ *
+ * png_set_packing(png_structrp png_ptr)
+ *
+ * This should only affect grayscale and palette images with less than 8 bits
+ * per pixel.
+ */
+static void
+image_transform_png_set_packing_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_packing(pp);
+ that->unpacked = 1;
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_packing_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ /* The general expand case depends on what the colour type is,
+ * low bit-depth pixel values are unpacked into bytes without
+ * scaling, so sample_depth is not changed.
+ */
+ if (that->bit_depth < 8) /* grayscale or palette */
+ that->bit_depth = 8;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_packing_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(colour_type)
+
+ this->next = *that;
+ *that = this;
+
+ /* Nothing should happen unless the bit depth is less than 8: */
+ return bit_depth < 8;
+}
+
+IT(packing);
+#undef PT
+#define PT ITSTRUCT(packing)
+
+#endif /* PNG_READ_PACK_SUPPORTED */
+
+/* png_set_packswap */
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+/* Swap pixels packed into bytes; reverses the order on screen so that
+ * the high order bits correspond to the rightmost pixels.
+ *
+ * png_set_packswap(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_packswap_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_packswap(pp);
+ that->this.littleendian = 1;
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_packswap_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->bit_depth < 8)
+ that->littleendian = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_packswap_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(colour_type)
+
+ this->next = *that;
+ *that = this;
+
+ return bit_depth < 8;
+}
+
+IT(packswap);
+#undef PT
+#define PT ITSTRUCT(packswap)
+
+#endif /* PNG_READ_PACKSWAP_SUPPORTED */
+
+
+/* png_set_invert_mono */
+#ifdef PNG_READ_INVERT_MONO_SUPPORTED
+/* Invert the gray channel
+ *
+ * png_set_invert_mono(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_invert_mono_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_invert_mono(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_invert_mono_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->colour_type & 4)
+ that->mono_inverted = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_invert_mono_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ /* Only has an effect on pixels with no colour: */
+ return (colour_type & 2) == 0;
+}
+
+IT(invert_mono);
+#undef PT
+#define PT ITSTRUCT(invert_mono)
+
+#endif /* PNG_READ_INVERT_MONO_SUPPORTED */
+
+#ifdef PNG_READ_SHIFT_SUPPORTED
+/* png_set_shift(png_structp, png_const_color_8p true_bits)
+ *
+ * The output pixels will be shifted by the given true_bits
+ * values.
+ */
+#define data ITDATA(shift)
+static png_color_8 data;
+
+static void
+image_transform_png_set_shift_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ /* Get a random set of shifts. The shifts need to do something
+ * to test the transform, so they are limited to the bit depth
+ * of the input image. Notice that in the following the 'gray'
+ * field is randomized independently. This acts as a check that
+ * libpng does use the correct field.
+ */
+ unsigned int depth = that->this.bit_depth;
+
+ data.red = (png_byte)/*SAFE*/(random_mod(depth)+1);
+ data.green = (png_byte)/*SAFE*/(random_mod(depth)+1);
+ data.blue = (png_byte)/*SAFE*/(random_mod(depth)+1);
+ data.gray = (png_byte)/*SAFE*/(random_mod(depth)+1);
+ data.alpha = (png_byte)/*SAFE*/(random_mod(depth)+1);
+ png_set_shift(pp, &data);
this->next->set(this->next, that, pp, pi);
}
static void
-image_transform_png_set_background_mod(PNG_CONST image_transform *this,
+image_transform_png_set_shift_mod(const image_transform *this,
image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
+ const transform_display *display)
{
- /* Check for tRNS first: */
- if (that->have_tRNS && that->colour_type != PNG_COLOR_TYPE_PALETTE)
- image_pixel_add_alpha(that, &display->this);
-
- /* This is only necessary if the alpha value is less than 1. */
- if (that->alphaf < 1)
+ /* Copy the correct values into the sBIT fields, libpng does not do
+ * anything to palette data:
+ */
+ if (that->colour_type != PNG_COLOR_TYPE_PALETTE)
{
- /* Now we do the background calculation without any gamma correction. */
- if (that->alphaf <= 0)
- {
- that->redf = data.redf;
- that->greenf = data.greenf;
- that->bluef = data.bluef;
+ that->sig_bits = 1;
- that->rede = data.rede;
- that->greene = data.greene;
- that->bluee = data.bluee;
+ /* The sBIT fields are reset to the values previously sent to
+ * png_set_shift according to the colour type.
+ * does.
+ */
+ if (that->colour_type & 2) /* RGB channels */
+ {
+ that->red_sBIT = data.red;
+ that->green_sBIT = data.green;
+ that->blue_sBIT = data.blue;
+ }
- that->red_sBIT= data.red_sBIT;
- that->green_sBIT= data.green_sBIT;
- that->blue_sBIT= data.blue_sBIT;
- }
+ else /* One grey channel */
+ that->red_sBIT = that->green_sBIT = that->blue_sBIT = data.gray;
- else /* 0 < alpha < 1 */
- {
- double alf = 1 - that->alphaf;
+ that->alpha_sBIT = data.alpha;
+ }
- that->redf = that->redf * that->alphaf + data.redf * alf;
- that->rede = that->rede * that->alphaf + data.rede * alf +
- DBL_EPSILON;
- that->greenf = that->greenf * that->alphaf + data.greenf * alf;
- that->greene = that->greene * that->alphaf + data.greene * alf +
- DBL_EPSILON;
- that->bluef = that->bluef * that->alphaf + data.bluef * alf;
- that->bluee = that->bluee * that->alphaf + data.bluee * alf +
- DBL_EPSILON;
- }
+ this->next->mod(this->next, that, pp, display);
+}
- /* Remove the alpha type and set the alpha (not in that order.) */
- that->alphaf = 1;
- that->alphae = 0;
+static int
+image_transform_png_set_shift_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
- if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
- that->colour_type = PNG_COLOR_TYPE_RGB;
- else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
- that->colour_type = PNG_COLOR_TYPE_GRAY;
- /* PNG_COLOR_TYPE_PALETTE is not changed */
- }
+ this->next = *that;
+ *that = this;
+
+ return colour_type != PNG_COLOR_TYPE_PALETTE;
+}
+
+IT(shift);
+#undef PT
+#define PT ITSTRUCT(shift)
+
+#endif /* PNG_READ_SHIFT_SUPPORTED */
+
+#ifdef THIS_IS_THE_PROFORMA
+static void
+image_transform_png_set_@_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_@(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+static void
+image_transform_png_set_@_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
this->next->mod(this->next, that, pp, display);
}
-#define image_transform_png_set_background_add image_transform_default_add
+static int
+image_transform_png_set_@_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ this->next = *that;
+ *that = this;
+
+ return 1;
+}
+
+IT(@);
+#endif
-#undef data
-IT(background);
-#undef PT
-#define PT ITSTRUCT(background)
-#endif /* PNG_READ_BACKGROUND_SUPPORTED */
/* This may just be 'end' if all the transforms are disabled! */
-static image_transform *PNG_CONST image_transform_first = &PT;
+static image_transform *const image_transform_first = &PT;
static void
-transform_enable(PNG_CONST char *name)
+transform_enable(const char *name)
{
/* Everything starts out enabled, so if we see an 'enable' disabled
* everything else the first time round.
}
static void
-transform_disable(PNG_CONST char *name)
+transform_disable(const char *name)
{
image_transform *list = image_transform_first;
static int
image_transform_test_counter(png_uint_32 counter, unsigned int max)
{
- /* Test the list to see if there is any point contining, given a current
+ /* Test the list to see if there is any point continuing, given a current
* counter and a 'max' value.
*/
image_transform *next = image_transform_first;
}
static png_uint_32
-image_transform_add(PNG_CONST image_transform **this, unsigned int max,
+image_transform_add(const image_transform **this, unsigned int max,
png_uint_32 counter, char *name, size_t sizeof_name, size_t *pos,
png_byte colour_type, png_byte bit_depth)
{
}
}
-#ifdef THIS_IS_THE_PROFORMA
-static void
-image_transform_png_set_@_set(PNG_CONST image_transform *this,
- transform_display *that, png_structp pp, png_infop pi)
-{
- png_set_@(pp);
- this->next->set(this->next, that, pp, pi);
-}
-
-static void
-image_transform_png_set_@_mod(PNG_CONST image_transform *this,
- image_pixel *that, png_const_structp pp,
- PNG_CONST transform_display *display)
-{
- this->next->mod(this->next, that, pp, display);
-}
-
-static int
-image_transform_png_set_@_add(image_transform *this,
- PNG_CONST image_transform **that, char *name, size_t sizeof_name,
- size_t *pos, png_byte colour_type, png_byte bit_depth)
-{
- this->next = *that;
- *that = this;
-
- *pos = safecat(name, sizeof_name, *pos, " +@");
-
- return 1;
-}
-
-IT(@);
-#endif
-
-/* png_set_quantize(png_structp, png_colorp palette, int num_palette,
- * int maximum_colors, png_const_uint_16p histogram, int full_quantize)
- *
- * Very difficult to validate this!
- */
-/*NOTE: TBD NYI */
-
-/* The data layout transforms are handled by swapping our own channel data,
- * necessarily these need to happen at the end of the transform list because the
- * semantic of the channels changes after these are executed. Some of these,
- * like set_shift and set_packing, can't be done at present because they change
- * the layout of the data at the sub-sample level so sample() won't get the
- * right answer.
- */
-/* png_set_invert_alpha */
-/*NOTE: TBD NYI */
-
-/* png_set_bgr */
-/*NOTE: TBD NYI */
-
-/* png_set_swap_alpha */
-/*NOTE: TBD NYI */
-
-/* png_set_swap */
-/*NOTE: TBD NYI */
-
-/* png_set_filler, (png_structp png_ptr, png_uint_32 filler, int flags)); */
-/*NOTE: TBD NYI */
-
-/* png_set_add_alpha, (png_structp png_ptr, png_uint_32 filler, int flags)); */
-/*NOTE: TBD NYI */
-
-/* png_set_packing */
-/*NOTE: TBD NYI */
-
-/* png_set_packswap */
-/*NOTE: TBD NYI */
-
-/* png_set_invert_mono */
-/*NOTE: TBD NYI */
-
-/* png_set_shift(png_structp, png_const_color_8p true_bits) */
-/*NOTE: TBD NYI */
-
static void
perform_transform_test(png_modifier *pm)
{
png_byte bit_depth = 0;
unsigned int palette_number = 0;
- while (next_format(&colour_type, &bit_depth, &palette_number, 0))
+ while (next_format(&colour_type, &bit_depth, &palette_number, pm->test_lbg,
+ pm->test_tRNS))
{
png_uint_32 counter = 0;
size_t base_pos;
for (;;)
{
size_t pos = base_pos;
- PNG_CONST image_transform *list = 0;
+ const image_transform *list = 0;
/* 'max' is currently hardwired to '1'; this should be settable on the
* command line.
gamma_display_init(gamma_display *dp, png_modifier *pm, png_uint_32 id,
double file_gamma, double screen_gamma, png_byte sbit, int threshold_test,
int use_input_precision, int scale16, int expand16,
- int do_background, PNG_CONST png_color_16 *pointer_to_the_background_color,
+ int do_background, const png_color_16 *pointer_to_the_background_color,
double background_gamma)
{
/* Standard fields */
- standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/,
+ standard_display_init(&dp->this, &pm->this, id, do_read_interlace,
pm->use_update_info);
/* Parameter fields */
/* If requested strip 16 to 8 bits - this is handled automagically below
* because the output bit depth is read from the library. Note that there
* are interactions with sBIT but, internally, libpng makes sbit at most
- * PNG_MAX_GAMMA_8 when doing the following.
+ * PNG_MAX_GAMMA_8 prior to 1.7 when doing the following.
*/
if (dp->scale16)
# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
int mode = dp->do_background - ALPHA_MODE_OFFSET;
/* The gamma value is the output gamma, and is in the standard,
- * non-inverted, represenation. It provides a default for the PNG file
+ * non-inverted, representation. It provides a default for the PNG file
* gamma, but since the file has a gAMA chunk this does not matter.
*/
- PNG_CONST double sg = dp->screen_gamma;
+ const double sg = dp->screen_gamma;
# ifndef PNG_FLOATING_POINT_SUPPORTED
- PNG_CONST png_fixed_point g = fix(sg);
+ png_fixed_point g = fix(sg);
# endif
# ifdef PNG_FLOATING_POINT_SUPPORTED
# ifdef PNG_READ_BACKGROUND_SUPPORTED
/* NOTE: this assumes the caller provided the correct background gamma!
*/
- PNG_CONST double bg = dp->background_gamma;
+ const double bg = dp->background_gamma;
# ifndef PNG_FLOATING_POINT_SUPPORTED
- PNG_CONST png_fixed_point g = fix(bg);
+ png_fixed_point g = fix(bg);
# endif
# ifdef PNG_FLOATING_POINT_SUPPORTED
init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp,
int in_depth, int out_depth)
{
- PNG_CONST unsigned int outmax = (1U<<out_depth)-1;
+ unsigned int outmax = (1U<<out_depth)-1;
vi->pp = pp;
vi->dp = dp;
vi->outlog = outlog(dp->pm, in_depth, out_depth);
if ((dp->this.colour_type & PNG_COLOR_MASK_ALPHA) != 0 ||
- (dp->this.colour_type == 3 && dp->this.is_transparent))
+ (dp->this.colour_type == 3 && dp->this.is_transparent) ||
+ ((dp->this.colour_type == 0 || dp->this.colour_type == 2) &&
+ dp->this.has_tRNS))
{
vi->do_background = dp->do_background;
if (vi->do_background != 0)
{
- PNG_CONST double bg_inverse = 1/dp->background_gamma;
+ const double bg_inverse = 1/dp->background_gamma;
double r, g, b;
/* Caller must at least put the gray value into the red channel */
vi->background_blue = b;
}
}
- else
+ else /* Do not expect any background processing */
vi->do_background = 0;
if (vi->do_background == 0)
/* This API returns the encoded *input* component, in the range 0..1 */
static double
-gamma_component_validate(PNG_CONST char *name, PNG_CONST validate_info *vi,
- PNG_CONST unsigned int id, PNG_CONST unsigned int od,
- PNG_CONST double alpha /* <0 for the alpha channel itself */,
- PNG_CONST double background /* component background value */)
+gamma_component_validate(const char *name, const validate_info *vi,
+ unsigned int id, unsigned int od,
+ const double alpha /* <0 for the alpha channel itself */,
+ const double background /* component background value */)
{
- PNG_CONST unsigned int isbit = id >> vi->isbit_shift;
- PNG_CONST unsigned int sbit_max = vi->sbit_max;
- PNG_CONST unsigned int outmax = vi->outmax;
- PNG_CONST int do_background = vi->do_background;
+ unsigned int isbit = id >> vi->isbit_shift;
+ unsigned int sbit_max = vi->sbit_max;
+ unsigned int outmax = vi->outmax;
+ int do_background = vi->do_background;
double i;
*
* pngvalid calculations:
* input_sample: linear result; i linearized and composed, range 0..1
- * encoded_sample: encoded result; input_sample scaled to ouput bit depth
+ * encoded_sample: encoded result; input_sample scaled to output bit depth
*
* libpng calculations:
* output: linear result; od scaled to 0..1 and linearized
case PNG_BACKGROUND_GAMMA_FILE:
case PNG_BACKGROUND_GAMMA_UNIQUE:
use_background = (alpha >= 0 && alpha < 1);
- /*FALL THROUGH*/
# endif
# ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ /* FALLTHROUGH */
case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
png_infop pi)
{
/* Get some constants derived from the input and output file formats: */
- PNG_CONST png_store* PNG_CONST ps = dp->this.ps;
- PNG_CONST png_byte in_ct = dp->this.colour_type;
- PNG_CONST png_byte in_bd = dp->this.bit_depth;
- PNG_CONST png_uint_32 w = dp->this.w;
- PNG_CONST png_uint_32 h = dp->this.h;
- PNG_CONST size_t cbRow = dp->this.cbRow;
- PNG_CONST png_byte out_ct = png_get_color_type(pp, pi);
- PNG_CONST png_byte out_bd = png_get_bit_depth(pp, pi);
+ const png_store* const ps = dp->this.ps;
+ png_byte in_ct = dp->this.colour_type;
+ png_byte in_bd = dp->this.bit_depth;
+ png_uint_32 w = dp->this.w;
+ png_uint_32 h = dp->this.h;
+ const size_t cbRow = dp->this.cbRow;
+ png_byte out_ct = png_get_color_type(pp, pi);
+ png_byte out_bd = png_get_bit_depth(pp, pi);
/* There are three sources of error, firstly the quantization in the
* file encoding, determined by sbit and/or the file depth, secondly
* Since the library must quantize the output to 8 or 16 bits there is
* a fundamental limit on the accuracy of the output of +/-.5 - this
* quantization limit is included in addition to the other limits
- * specified by the paramaters to the API. (Effectively, add .5
+ * specified by the parameters to the API. (Effectively, add .5
* everywhere.)
*
- * The behavior of the 'sbit' paramter is defined by section 12.5
+ * The behavior of the 'sbit' parameter is defined by section 12.5
* (sample depth scaling) of the PNG spec. That section forces the
* decoder to assume that the PNG values have been scaled if sBIT is
* present:
* The basic tests below do not do this, however if 'use_input_precision'
* is set a subsequent test is performed above.
*/
- PNG_CONST unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U;
+ unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U;
int processing;
png_uint_32 y;
- PNG_CONST store_palette_entry *in_palette = dp->this.palette;
- PNG_CONST int in_is_transparent = dp->this.is_transparent;
+ const store_palette_entry *in_palette = dp->this.palette;
+ int in_is_transparent = dp->this.is_transparent;
+ int process_tRNS;
int out_npalette = -1;
int out_is_transparent = 0; /* Just refers to the palette case */
store_palette out_palette;
processing = (vi.gamma_correction > 0 && !dp->threshold_test)
|| in_bd != out_bd || in_ct != out_ct || vi.do_background;
+ process_tRNS = dp->this.has_tRNS && vi.do_background;
/* TODO: FIX THIS: MAJOR BUG! If the transformations all happen inside
* the palette there is no way of finding out, because libpng fails to
double alpha = 1; /* serves as a flag value */
/* Record the palette index for index images. */
- PNG_CONST unsigned int in_index =
- in_ct == 3 ? sample(std, 3, in_bd, x, 0) : 256;
- PNG_CONST unsigned int out_index =
- out_ct == 3 ? sample(std, 3, out_bd, x, 0) : 256;
+ unsigned int in_index =
+ in_ct == 3 ? sample(std, 3, in_bd, x, 0, 0, 0) : 256;
+ unsigned int out_index =
+ out_ct == 3 ? sample(std, 3, out_bd, x, 0, 0, 0) : 256;
/* Handle input alpha - png_set_background will cause the output
* alpha to disappear so there is nothing to check.
*/
- if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 || (in_ct == 3 &&
- in_is_transparent))
+ if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 ||
+ (in_ct == 3 && in_is_transparent))
{
- PNG_CONST unsigned int input_alpha = in_ct == 3 ?
+ unsigned int input_alpha = in_ct == 3 ?
dp->this.palette[in_index].alpha :
- sample(std, in_ct, in_bd, x, samples_per_pixel);
+ sample(std, in_ct, in_bd, x, samples_per_pixel, 0, 0);
unsigned int output_alpha = 65536 /* as a flag value */;
else if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0)
output_alpha = sample(pRow, out_ct, out_bd, x,
- samples_per_pixel);
+ samples_per_pixel, 0, 0);
if (output_alpha != 65536)
alpha = gamma_component_validate("alpha", &vi, input_alpha,
}
}
+ else if (process_tRNS)
+ {
+ /* alpha needs to be set appropriately for this pixel, it is
+ * currently 1 and needs to be 0 for an input pixel which matches
+ * the values in tRNS.
+ */
+ switch (in_ct)
+ {
+ case 0: /* gray */
+ if (sample(std, in_ct, in_bd, x, 0, 0, 0) ==
+ dp->this.transparent.red)
+ alpha = 0;
+ break;
+
+ case 2: /* RGB */
+ if (sample(std, in_ct, in_bd, x, 0, 0, 0) ==
+ dp->this.transparent.red &&
+ sample(std, in_ct, in_bd, x, 1, 0, 0) ==
+ dp->this.transparent.green &&
+ sample(std, in_ct, in_bd, x, 2, 0, 0) ==
+ dp->this.transparent.blue)
+ alpha = 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+
/* Handle grayscale or RGB components. */
if ((in_ct & PNG_COLOR_MASK_COLOR) == 0) /* grayscale */
(void)gamma_component_validate("gray", &vi,
- sample(std, in_ct, in_bd, x, 0),
- sample(pRow, out_ct, out_bd, x, 0), alpha/*component*/,
- vi.background_red);
+ sample(std, in_ct, in_bd, x, 0, 0, 0),
+ sample(pRow, out_ct, out_bd, x, 0, 0, 0),
+ alpha/*component*/, vi.background_red);
else /* RGB or palette */
{
(void)gamma_component_validate("red", &vi,
in_ct == 3 ? in_palette[in_index].red :
- sample(std, in_ct, in_bd, x, 0),
+ sample(std, in_ct, in_bd, x, 0, 0, 0),
out_ct == 3 ? out_palette[out_index].red :
- sample(pRow, out_ct, out_bd, x, 0),
+ sample(pRow, out_ct, out_bd, x, 0, 0, 0),
alpha/*component*/, vi.background_red);
(void)gamma_component_validate("green", &vi,
in_ct == 3 ? in_palette[in_index].green :
- sample(std, in_ct, in_bd, x, 1),
+ sample(std, in_ct, in_bd, x, 1, 0, 0),
out_ct == 3 ? out_palette[out_index].green :
- sample(pRow, out_ct, out_bd, x, 1),
+ sample(pRow, out_ct, out_bd, x, 1, 0, 0),
alpha/*component*/, vi.background_green);
(void)gamma_component_validate("blue", &vi,
in_ct == 3 ? in_palette[in_index].blue :
- sample(std, in_ct, in_bd, x, 2),
+ sample(std, in_ct, in_bd, x, 2, 0, 0),
out_ct == 3 ? out_palette[out_index].blue :
- sample(pRow, out_ct, out_bd, x, 2),
+ sample(pRow, out_ct, out_bd, x, 2, 0, 0),
alpha/*component*/, vi.background_blue);
}
}
* maxpc: maximum percentage error (as a percentage)
*/
static void
-gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn,
- PNG_CONST png_byte bit_depthIn, PNG_CONST int palette_numberIn,
- PNG_CONST int interlace_typeIn,
- PNG_CONST double file_gammaIn, PNG_CONST double screen_gammaIn,
- PNG_CONST png_byte sbitIn, PNG_CONST int threshold_testIn,
- PNG_CONST char *name,
- PNG_CONST int use_input_precisionIn, PNG_CONST int scale16In,
- PNG_CONST int expand16In, PNG_CONST int do_backgroundIn,
- PNG_CONST png_color_16 *bkgd_colorIn, double bkgd_gammaIn)
+gamma_test(png_modifier *pmIn, png_byte colour_typeIn,
+ png_byte bit_depthIn, int palette_numberIn,
+ int interlace_typeIn,
+ const double file_gammaIn, const double screen_gammaIn,
+ png_byte sbitIn, int threshold_testIn,
+ const char *name,
+ int use_input_precisionIn, int scale16In,
+ int expand16In, int do_backgroundIn,
+ const png_color_16 *bkgd_colorIn, double bkgd_gammaIn)
{
gamma_display d;
context(&pmIn->this, fault);
modification_reset(d.pm->modifications);
- /* Get a png_struct for writing the image. */
+ /* Get a png_struct for reading the image. */
pp = set_modifier_for_read(d.pm, &pi, d.this.id, name);
standard_palette_init(&d.this);
/* Don't test more than one instance of each palette - it's pointless, in
* fact this test is somewhat excessive since libpng doesn't make this
* decision based on colour type or bit depth!
+ *
+ * CHANGED: now test two palettes and, as a side effect, images with and
+ * without tRNS.
*/
- while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/))
- if (palette_number == 0)
+ while (next_format(&colour_type, &bit_depth, &palette_number,
+ pm->test_lbg_gamma_threshold, pm->test_tRNS))
+ if (palette_number < 2)
{
double test_gamma = 1.0;
while (test_gamma >= .4)
}
static void gamma_transform_test(png_modifier *pm,
- PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth,
- PNG_CONST int palette_number,
- PNG_CONST int interlace_type, PNG_CONST double file_gamma,
- PNG_CONST double screen_gamma, PNG_CONST png_byte sbit,
- PNG_CONST int use_input_precision, PNG_CONST int scale16)
+ png_byte colour_type, png_byte bit_depth,
+ int palette_number,
+ int interlace_type, const double file_gamma,
+ const double screen_gamma, png_byte sbit,
+ int use_input_precision, int scale16)
{
size_t pos = 0;
char name[64];
png_byte bit_depth = 0;
unsigned int palette_number = 0;
- while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/))
+ while (next_format(&colour_type, &bit_depth, &palette_number,
+ pm->test_lbg_gamma_transform, pm->test_tRNS))
{
unsigned int i, j;
- for (i=0; i<pm->ngamma_tests; ++i) for (j=0; j<pm->ngamma_tests; ++j)
- if (i != j)
+ for (i=0; i<pm->ngamma_tests; ++i)
+ {
+ for (j=0; j<pm->ngamma_tests; ++j)
{
- gamma_transform_test(pm, colour_type, bit_depth, palette_number,
- pm->interlace_type, 1/pm->gammas[i], pm->gammas[j], 0/*sBIT*/,
- pm->use_input_precision, 0 /*do not scale16*/);
+ if (i != j)
+ {
+ gamma_transform_test(pm, colour_type, bit_depth, palette_number,
+ pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
+ 0/*sBIT*/, pm->use_input_precision, 0/*do not scale16*/);
- if (fail(pm))
- return;
+ if (fail(pm))
+ return;
+ }
}
+ }
}
}
png_byte colour_type = 0, bit_depth = 0;
unsigned int npalette = 0;
- while (next_format(&colour_type, &bit_depth, &npalette, 1/*gamma*/))
+ while (next_format(&colour_type, &bit_depth, &npalette,
+ pm->test_lbg_gamma_sbit, pm->test_tRNS))
if ((colour_type & PNG_COLOR_MASK_ALPHA) == 0 &&
((colour_type == 3 && sbit < 8) ||
(colour_type != 3 && sbit < bit_depth)))
{
unsigned int j;
- for (j=0; j<pm->ngamma_tests; ++j) if (i != j)
+ for (j=0; j<pm->ngamma_tests; ++j)
{
- gamma_transform_test(pm, colour_type, bit_depth, npalette,
- pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
- sbit, pm->use_input_precision_sbit, 0 /*scale16*/);
+ if (i != j)
+ {
+ gamma_transform_test(pm, colour_type, bit_depth, npalette,
+ pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
+ sbit, pm->use_input_precision_sbit, 0 /*scale16*/);
- if (fail(pm))
- return;
+ if (fail(pm))
+ return;
+ }
}
}
}
# ifndef PNG_MAX_GAMMA_8
# define PNG_MAX_GAMMA_8 11
# endif
-# define SBIT_16_TO_8 PNG_MAX_GAMMA_8
+# if defined PNG_MAX_GAMMA_8 || PNG_LIBPNG_VER < 10700
+# define SBIT_16_TO_8 PNG_MAX_GAMMA_8
+# else
+# define SBIT_16_TO_8 16
+# endif
/* Include the alpha cases here. Note that sbit matches the internal value
* used by the library - otherwise we will get spurious errors from the
* internal sbit style approximation.
#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
defined(PNG_READ_ALPHA_MODE_SUPPORTED)
static void gamma_composition_test(png_modifier *pm,
- PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth,
- PNG_CONST int palette_number,
- PNG_CONST int interlace_type, PNG_CONST double file_gamma,
- PNG_CONST double screen_gamma,
- PNG_CONST int use_input_precision, PNG_CONST int do_background,
- PNG_CONST int expand_16)
+ png_byte colour_type, png_byte bit_depth,
+ int palette_number,
+ int interlace_type, const double file_gamma,
+ const double screen_gamma,
+ int use_input_precision, int do_background,
+ int expand_16)
{
size_t pos = 0;
png_const_charp base;
}
background.index = 193; /* rgb(193,193,193) to detect errors */
+
if (!(colour_type & PNG_COLOR_MASK_COLOR))
{
+ /* Because, currently, png_set_background is always called with
+ * 'need_expand' false in this case and because the gamma test itself
+ * doesn't cause an expand to 8-bit for lower bit depths the colour must
+ * be reduced to the correct range.
+ */
+ if (bit_depth < 8)
+ background.gray &= (png_uint_16)((1U << bit_depth)-1);
+
/* Grayscale input, we do not convert to RGB (TBD), so we must set the
* background to gray - else libpng seems to fail.
*/
/* Skip the non-alpha cases - there is no setting of a transparency colour at
* present.
- */
- while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/))
- if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0)
+ *
+ * TODO: incorrect; the palette case sets tRNS and, now RGB and gray do,
+ * however the palette case fails miserably so is commented out below.
+ */
+ while (next_format(&colour_type, &bit_depth, &palette_number,
+ pm->test_lbg_gamma_composition, pm->test_tRNS))
+ if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0
+#if 0 /* TODO: FIXME */
+ /*TODO: FIXME: this should work */
+ || colour_type == 3
+#endif
+ || (colour_type != 3 && palette_number != 0))
{
unsigned int i, j;
/* Don't skip the i==j case here - it's relevant. */
- for (i=0; i<pm->ngamma_tests; ++i) for (j=0; j<pm->ngamma_tests; ++j)
+ for (i=0; i<pm->ngamma_tests; ++i)
{
- gamma_composition_test(pm, colour_type, bit_depth, palette_number,
- pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
- pm->use_input_precision, do_background, expand_16);
+ for (j=0; j<pm->ngamma_tests; ++j)
+ {
+ gamma_composition_test(pm, colour_type, bit_depth, palette_number,
+ pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
+ pm->use_input_precision, do_background, expand_16);
- if (fail(pm))
- return;
+ if (fail(pm))
+ return;
+ }
}
}
}
pm->calculations_use_input_precision = 0;
if (summary)
- summarize_gamma_errors(pm, 0/*who*/, 1/*low bit depth*/, 1/*indexed*/);
+ summarize_gamma_errors(pm, NULL/*who*/, 1/*low bit depth*/, 1/*indexed*/);
if (fail(pm))
return;
* be indexed adam7[y][x] and notice that the pass numbers are based at
* 1, not 0 - the base libpng uses.
*/
-static PNG_CONST
+static const
png_byte adam7[8][8] =
{
{ 1,6,4,6,2,6,4,6 },
{
int x, y;
++pass;
- for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
- return y;
+ for (y=0; y<8; ++y)
+ for (x=0; x<8; ++x)
+ if (adam7[y][x] == pass)
+ return y;
return 0xf;
}
{
int x, y;
++pass;
- for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
- return x;
+ for (x=0; x<8; ++x)
+ for (y=0; y<8; ++y)
+ if (adam7[y][x] == pass)
+ return x;
return 0xf;
}
{
int x, y, base=(-1), inc=8;
++pass;
- for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+ for (y=0; y<8; ++y)
{
- if (base == (-1))
- base = y;
- else if (base == y)
- {}
- else if (inc == y-base)
- base=y;
- else if (inc == 8)
- inc = y-base, base=y;
- else if (inc != y-base)
- return 0xff; /* error - more than one 'inc' value! */
+ for (x=0; x<8; ++x)
+ {
+ if (adam7[y][x] == pass)
+ {
+ if (base == (-1))
+ base = y;
+ else if (base == y)
+ {}
+ else if (inc == y-base)
+ base=y;
+ else if (inc == 8)
+ inc = y-base, base=y;
+ else if (inc != y-base)
+ return 0xff; /* error - more than one 'inc' value! */
+ }
+ }
}
if (base == (-1)) return 0xfe; /* error - no row in pass! */
{
int x, y, base=(-1), inc=8;
++pass;
- for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+ for (x=0; x<8; ++x)
{
- if (base == (-1))
- base = x;
- else if (base == x)
- {}
- else if (inc == x-base)
- base=x;
- else if (inc == 8)
- inc = x-base, base=x;
- else if (inc != x-base)
- return 0xff; /* error - more than one 'inc' value! */
+ for (y=0; y<8; ++y)
+ {
+ if (adam7[y][x] == pass)
+ {
+ if (base == (-1))
+ base = x;
+ else if (base == x)
+ {}
+ else if (inc == x-base)
+ base=x;
+ else if (inc == 8)
+ inc = x-base, base=x;
+ else if (inc != x-base)
+ return 0xff; /* error - more than one 'inc' value! */
+ }
+ }
}
if (base == (-1)) return 0xfe; /* error - no row in pass! */
int x;
y &= 7;
++pass;
- for (x=0; x<8; ++x) if (adam7[y][x] == pass)
- return 1;
+ for (x=0; x<8; ++x)
+ if (adam7[y][x] == pass)
+ return 1;
return 0;
}
int y;
x &= 7;
++pass;
- for (y=0; y<8; ++y) if (adam7[y][x] == pass)
- return 1;
+ for (y=0; y<8; ++y)
+ if (adam7[y][x] == pass)
+ return 1;
return 0;
}
height &= 7;
++pass;
- for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+ for (y=0; y<8; ++y)
{
- rows += tiles;
- if (y < height) ++rows;
- break; /* i.e. break the 'x', column, loop. */
+ for (x=0; x<8; ++x)
+ {
+ if (adam7[y][x] == pass)
+ {
+ rows += tiles;
+ if (y < height) ++rows;
+ break; /* i.e. break the 'x', column, loop. */
+ }
+ }
}
return rows;
width &= 7;
++pass;
- for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+ for (x=0; x<8; ++x)
{
- cols += tiles;
- if (x < width) ++cols;
- break; /* i.e. break the 'y', row, loop. */
+ for (y=0; y<8; ++y)
+ {
+ if (adam7[y][x] == pass)
+ {
+ cols += tiles;
+ if (x < width) ++cols;
+ break; /* i.e. break the 'y', row, loop. */
+ }
+ }
}
return cols;
*/
for (v=0;;)
{
+ /* The first two tests overflow if the pass row or column is outside
+ * the possible range for a 32-bit result. In fact the values should
+ * never be outside the range for a 31-bit result, but checking for 32
+ * bits here ensures that if an app uses a bogus pass row or column
+ * (just so long as it fits in a 32 bit integer) it won't get a
+ * possibly dangerous overflow.
+ */
/* First the base 0 stuff: */
- m = PNG_ROW_FROM_PASS_ROW(v, pass);
- f = png_row_from_pass_row(v, pass);
- if (m != f)
+ if (v < png_pass_rows(0xFFFFFFFFU, pass))
{
- fprintf(stderr, "PNG_ROW_FROM_PASS_ROW(%u, %d) = %u != %x\n",
- v, pass, m, f);
- exit(99);
+ m = PNG_ROW_FROM_PASS_ROW(v, pass);
+ f = png_row_from_pass_row(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_ROW_FROM_PASS_ROW(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(99);
+ }
}
- m = PNG_COL_FROM_PASS_COL(v, pass);
- f = png_col_from_pass_col(v, pass);
- if (m != f)
+ if (v < png_pass_cols(0xFFFFFFFFU, pass))
{
- fprintf(stderr, "PNG_COL_FROM_PASS_COL(%u, %d) = %u != %x\n",
- v, pass, m, f);
- exit(99);
+ m = PNG_COL_FROM_PASS_COL(v, pass);
+ f = png_col_from_pass_col(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_COL_FROM_PASS_COL(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(99);
+ }
}
m = PNG_ROW_IN_INTERLACE_PASS(v, pass);
* The png_modifier code assumes that encodings[0] is sRGB and treats it
* specially: do not change the first entry in this list!
*/
-static PNG_CONST color_encoding test_encodings[] =
+static const color_encoding test_encodings[] =
{
/* sRGB: must be first in this list! */
/*gamma:*/ { 1/2.2,
/*red: */ { 0.716500716779386, 0.258728243040113, 0.000000000000000 },
/*green:*/ { 0.101020574397477, 0.724682314948566, 0.051211818965388 },
/*blue: */ { 0.146774385252705, 0.016589442011321, 0.773892783545073} },
+/* Fake encoding which selects just the green channel */
+/*gamma:*/ { 1.45/2.2, /* the 'Mac' gamma */
+/*red: */ { 0.716500716779386, 0.000000000000000, 0.000000000000000 },
+/*green:*/ { 0.101020574397477, 1.000000000000000, 0.051211818965388 },
+/*blue: */ { 0.146774385252705, 0.000000000000000, 0.773892783545073} },
};
/* signal handler
/* main program */
int main(int argc, char **argv)
{
- volatile int summary = 1; /* Print the error summary at the end */
- volatile int memstats = 0; /* Print memory statistics at the end */
+ int summary = 1; /* Print the error summary at the end */
+ int memstats = 0; /* Print memory statistics at the end */
/* Create the given output file on success: */
- PNG_CONST char *volatile touch = NULL;
+ const char *touch = NULL;
/* This is an array of standard gamma values (believe it or not I've seen
* every one of these mentioned somewhere.)
anon_context(&pm.this);
+ gnu_volatile(summary)
+ gnu_volatile(memstats)
+ gnu_volatile(touch)
+
/* Add appropriate signal handlers, just the ANSI specified ones: */
signal(SIGABRT, signal_handler);
signal(SIGFPE, signal_handler);
/* Store the test gammas */
pm.gammas = gammas;
- pm.ngammas = (sizeof gammas) / (sizeof gammas[0]);
+ pm.ngammas = ARRAY_SIZE(gammas);
pm.ngamma_tests = 0; /* default to off */
+ /* Low bit depth gray images don't do well in the gamma tests, until
+ * this is fixed turn them off for some gamma cases:
+ */
+# ifdef PNG_WRITE_tRNS_SUPPORTED
+ pm.test_tRNS = 1;
+# endif
+ pm.test_lbg = PNG_LIBPNG_VER >= 10600;
+ pm.test_lbg_gamma_threshold = 1;
+ pm.test_lbg_gamma_transform = PNG_LIBPNG_VER >= 10600;
+ pm.test_lbg_gamma_sbit = 1;
+ pm.test_lbg_gamma_composition = PNG_LIBPNG_VER >= 10700;
+
/* And the test encodings */
pm.encodings = test_encodings;
- pm.nencodings = (sizeof test_encodings) / (sizeof test_encodings[0]);
+ pm.nencodings = ARRAY_SIZE(test_encodings);
- pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */
+# if PNG_LIBPNG_VER < 10700
+ pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */
+# else
+ pm.sbitlow = 1U;
+# endif
/* The following allows results to pass if they correspond to anything in the
* transformed range [input-.5,input+.5]; this is is required because of the
- * way libpng treates the 16_TO_8 flag when building the gamma tables in
+ * way libpng treats the 16_TO_8 flag when building the gamma tables in
* releases up to 1.6.0.
*
* TODO: review this
/* Some default values (set the behavior for 'make check' here).
* These values simply control the maximum error permitted in the gamma
- * transformations. The practial limits for human perception are described
+ * transformations. The practical limits for human perception are described
* below (the setting for maxpc16), however for 8 bit encodings it isn't
* possible to meet the accepted capabilities of human vision - i.e. 8 bit
* images can never be good enough, regardless of encoding.
pm.maxout16 = .499; /* Error in *encoded* value */
pm.maxabs16 = .00005;/* 1/20000 */
pm.maxcalc16 =1./65535;/* +/-1 in 16 bits for compose errors */
- pm.maxcalcG = 1./((1<<PNG_MAX_GAMMA_8)-1);
+# if PNG_LIBPNG_VER < 10700
+ pm.maxcalcG = 1./((1<<PNG_MAX_GAMMA_8)-1);
+# else
+ pm.maxcalcG = 1./((1<<16)-1);
+# endif
/* NOTE: this is a reasonable perceptual limit. We assume that humans can
* perceive light level differences of 1% over a 100:1 range, so we need to
else if (strcmp(*argv, "-q") == 0)
summary = pm.this.verbose = pm.log = 0;
- else if (strcmp(*argv, "-w") == 0)
+ else if (strcmp(*argv, "-w") == 0 ||
+ strcmp(*argv, "--strict") == 0)
+ pm.this.treat_warnings_as_errors = 1; /* NOTE: this is the default! */
+
+ else if (strcmp(*argv, "--nostrict") == 0)
pm.this.treat_warnings_as_errors = 0;
else if (strcmp(*argv, "--speed") == 0)
#ifdef PNG_READ_TRANSFORMS_SUPPORTED
else if (strncmp(*argv, "--transform-disable=",
sizeof "--transform-disable") == 0)
- {
+ {
pm.test_transform = 1;
transform_disable(*argv + sizeof "--transform-disable");
- }
+ }
else if (strncmp(*argv, "--transform-enable=",
sizeof "--transform-enable") == 0)
- {
+ {
pm.test_transform = 1;
transform_enable(*argv + sizeof "--transform-enable");
- }
+ }
#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
else if (strcmp(*argv, "--gamma") == 0)
- {
+ {
/* Just do two gamma tests here (2.2 and linear) for speed: */
pm.ngamma_tests = 2U;
pm.test_gamma_threshold = 1;
pm.test_gamma_transform = 1;
pm.test_gamma_sbit = 1;
pm.test_gamma_scale16 = 1;
- pm.test_gamma_background = 1;
+ pm.test_gamma_background = 1; /* composition */
pm.test_gamma_alpha_mode = 1;
- }
+ }
else if (strcmp(*argv, "--nogamma") == 0)
pm.ngamma_tests = 0;
else if (strcmp(*argv, "--noexpand16") == 0)
pm.test_gamma_expand16 = 0;
+ else if (strcmp(*argv, "--low-depth-gray") == 0)
+ pm.test_lbg = pm.test_lbg_gamma_threshold =
+ pm.test_lbg_gamma_transform = pm.test_lbg_gamma_sbit =
+ pm.test_lbg_gamma_composition = 1;
+
+ else if (strcmp(*argv, "--nolow-depth-gray") == 0)
+ pm.test_lbg = pm.test_lbg_gamma_threshold =
+ pm.test_lbg_gamma_transform = pm.test_lbg_gamma_sbit =
+ pm.test_lbg_gamma_composition = 0;
+
+# ifdef PNG_WRITE_tRNS_SUPPORTED
+ else if (strcmp(*argv, "--tRNS") == 0)
+ pm.test_tRNS = 1;
+# endif
+
+ else if (strcmp(*argv, "--notRNS") == 0)
+ pm.test_tRNS = 0;
+
else if (strcmp(*argv, "--more-gammas") == 0)
pm.ngamma_tests = 3U;
else if (strcmp(*argv, "--interlace") == 0)
{
-# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+# if CAN_WRITE_INTERLACE
pm.interlace_type = PNG_INTERLACE_ADAM7;
-# else
+# else /* !CAN_WRITE_INTERLACE */
fprintf(stderr, "pngvalid: no write interlace support\n");
return SKIP;
-# endif
+# endif /* !CAN_WRITE_INTERLACE */
}
else if (strcmp(*argv, "--use-input-precision") == 0)
const char *arg = 9+*argv;
unsigned char option=0, setting=0;
-#ifdef PNG_ARM_NEON_API_SUPPORTED
+#ifdef PNG_ARM_NEON
if (strncmp(arg, "arm-neon:", 9) == 0)
option = PNG_ARM_NEON, arg += 9;
else
#endif
+#ifdef PNG_EXTENSIONS
+ if (strncmp(arg, "extensions:", 11) == 0)
+ option = PNG_EXTENSIONS, arg += 11;
+
+ else
+#endif
#ifdef PNG_MAXIMUM_INFLATE_WINDOW
if (strncmp(arg, "max-inflate-window:", 19) == 0)
option = PNG_MAXIMUM_INFLATE_WINDOW, arg += 19;
Try
{
/* Make useful base images */
- make_transform_images(&pm.this);
+ make_transform_images(&pm);
/* Perform the standard and gamma tests. */
if (pm.test_standard)