| |
| /* pngvalid.c - validate libpng by constructing then reading png files. |
| * |
| * Last changed in libpng 1.5.25 [December 3, 2015] |
| * Copyright (c) 2014-2015 Glenn Randers-Pehrson |
| * Written by John Cunningham Bowler |
| * |
| * This code is released under the libpng license. |
| * For conditions of distribution and use, see the disclaimer |
| * and license in png.h |
| * |
| * NOTES: |
| * This is a C program that is intended to be linked against libpng. It |
| * generates bitmaps internally, stores them as PNG files (using the |
| * sequential write code) then reads them back (using the sequential |
| * read code) and validates that the result has the correct data. |
| * |
| * The program can be modified and extended to test the correctness of |
| * transformations performed by libpng. |
| */ |
| |
| #define _POSIX_SOURCE 1 |
| #define _ISOC99_SOURCE 1 /* For floating point */ |
| #define _GNU_SOURCE 1 /* For the floating point exception extension */ |
| |
| #include <signal.h> |
| #include <stdio.h> |
| |
| #if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H) |
| # include <config.h> |
| #endif |
| |
| #ifdef HAVE_FEENABLEEXCEPT /* from config.h, if included */ |
| # 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: |
| */ |
| #ifdef PNG_FREESTANDING_TESTS |
| # include <png.h> |
| #else |
| # include "../../png.h" |
| #endif |
| |
| #ifdef PNG_ZLIB_HEADER |
| # include PNG_ZLIB_HEADER |
| #else |
| # include <zlib.h> /* For crc32 */ |
| #endif |
| |
| /* 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 |
| # define SKIP 77 |
| #endif |
| |
| /* pngvalid requires write support and one of the fixed or floating point APIs. |
| */ |
| #if defined(PNG_WRITE_SUPPORTED) &&\ |
| (defined(PNG_FIXED_POINT_SUPPORTED) || defined(PNG_FLOATING_POINT_SUPPORTED)) |
| |
| #if PNG_LIBPNG_VER < 10500 |
| /* This deliberately lacks the const. */ |
| typedef png_byte *png_const_bytep; |
| |
| /* This is copied from 1.5.1 png.h: */ |
| #define PNG_INTERLACE_ADAM7_PASSES 7 |
| #define PNG_PASS_START_ROW(pass) (((1U&~(pass))<<(3-((pass)>>1)))&7) |
| #define PNG_PASS_START_COL(pass) (((1U& (pass))<<(3-(((pass)+1)>>1)))&7) |
| #define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) |
| #define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) |
| #define PNG_PASS_ROWS(height, pass) (((height)+(((1<<PNG_PASS_ROW_SHIFT(pass))\ |
| -1)-PNG_PASS_START_ROW(pass)))>>PNG_PASS_ROW_SHIFT(pass)) |
| #define PNG_PASS_COLS(width, pass) (((width)+(((1<<PNG_PASS_COL_SHIFT(pass))\ |
| -1)-PNG_PASS_START_COL(pass)))>>PNG_PASS_COL_SHIFT(pass)) |
| #define PNG_ROW_FROM_PASS_ROW(yIn, pass) \ |
| (((yIn)<<PNG_PASS_ROW_SHIFT(pass))+PNG_PASS_START_ROW(pass)) |
| #define PNG_COL_FROM_PASS_COL(xIn, pass) \ |
| (((xIn)<<PNG_PASS_COL_SHIFT(pass))+PNG_PASS_START_COL(pass)) |
| #define PNG_PASS_MASK(pass,off) ( \ |
| ((0x110145AFU>>(((7-(off))-(pass))<<2)) & 0xFU) | \ |
| ((0x01145AF0U>>(((7-(off))-(pass))<<2)) & 0xF0U)) |
| #define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ |
| ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) |
| #define PNG_COL_IN_INTERLACE_PASS(x, pass) \ |
| ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) |
| |
| /* These are needed too for the default build: */ |
| #define PNG_WRITE_16BIT_SUPPORTED |
| #define PNG_READ_16BIT_SUPPORTED |
| |
| /* This comes from pnglibconf.h afer 1.5: */ |
| #define PNG_FP_1 100000 |
| #define PNG_GAMMA_THRESHOLD_FIXED\ |
| ((png_fixed_point)(PNG_GAMMA_THRESHOLD * PNG_FP_1)) |
| #endif |
| |
| #if PNG_LIBPNG_VER < 10600 |
| /* 1.6.0 constifies many APIs, the following exists to allow pngvalid to be |
| * compiled against earlier versions. |
| */ |
| # define png_const_structp png_structp |
| #endif |
| |
| #include <float.h> /* For floating point constants */ |
| #include <stdlib.h> /* For malloc */ |
| #include <string.h> /* For memcpy, memset */ |
| #include <math.h> /* For floor */ |
| |
| /* Unused formal parameter errors are removed using the following macro which is |
| * expected to have no bad effects on performance. |
| */ |
| #ifndef UNUSED |
| # if defined(__GNUC__) || defined(_MSC_VER) |
| # define UNUSED(param) (void)param; |
| # else |
| # define UNUSED(param) |
| # endif |
| #endif |
| |
| /***************************** EXCEPTION HANDLING *****************************/ |
| #ifdef PNG_FREESTANDING_TESTS |
| # include <cexcept.h> |
| #else |
| # include "../visupng/cexcept.h" |
| #endif |
| |
| #ifdef __cplusplus |
| # define this not_the_cpp_this |
| # define new not_the_cpp_new |
| # define voidcast(type, value) static_cast<type>(value) |
| #else |
| # define voidcast(type, value) (value) |
| #endif /* __cplusplus */ |
| |
| struct png_store; |
| define_exception_type(struct png_store*); |
| |
| /* The following are macros to reduce typing everywhere where the well known |
| * name 'the_exception_context' must be defined. |
| */ |
| #define anon_context(ps) struct exception_context *the_exception_context = \ |
| &(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 |
| * minor errors crashing. The following functions deal with one very |
| * common class of errors in error handlers - attempting to format error or |
| * warning messages into buffers that are too small. |
| */ |
| static size_t safecat(char *buffer, size_t bufsize, size_t pos, |
| const char *cat) |
| { |
| while (pos < bufsize && cat != NULL && *cat != 0) |
| buffer[pos++] = *cat++; |
| |
| if (pos >= bufsize) |
| pos = bufsize-1; |
| |
| buffer[pos] = 0; |
| return pos; |
| } |
| |
| static size_t safecatn(char *buffer, size_t bufsize, size_t pos, int n) |
| { |
| char number[64]; |
| sprintf(number, "%d", n); |
| return safecat(buffer, bufsize, pos, number); |
| } |
| |
| #ifdef PNG_READ_TRANSFORMS_SUPPORTED |
| static size_t safecatd(char *buffer, size_t bufsize, size_t pos, double d, |
| int precision) |
| { |
| char number[64]; |
| sprintf(number, "%.*f", precision, d); |
| return safecat(buffer, bufsize, pos, number); |
| } |
| #endif |
| |
| static const char invalid[] = "invalid"; |
| static const char sep[] = ": "; |
| |
| static const char *colour_types[8] = |
| { |
| "grayscale", invalid, "truecolour", "indexed-colour", |
| "grayscale with alpha", invalid, "truecolour with alpha", invalid |
| }; |
| |
| #ifdef PNG_READ_TRANSFORMS_SUPPORTED |
| /* Convert a double precision value to fixed point. */ |
| static png_fixed_point |
| fix(double d) |
| { |
| d = floor(d * PNG_FP_1 + .5); |
| return (png_fixed_point)d; |
| } |
| #endif /* PNG_READ_SUPPORTED */ |
| |
| /* Generate random bytes. This uses a boring repeatable algorithm and it |
| * is implemented here so that it gives the same set of numbers on every |
| * architecture. It's a linear congruential generator (Knuth or Sedgewick |
| * "Algorithms") but it comes from the 'feedback taps' table in Horowitz and |
| * Hill, "The Art of Electronics" (Pseudo-Random Bit Sequences and Noise |
| * Generation.) |
| */ |
| static void |
| make_random_bytes(png_uint_32* seed, void* pv, size_t size) |
| { |
| png_uint_32 u0 = seed[0], u1 = seed[1]; |
| png_bytep bytes = voidcast(png_bytep, pv); |
| |
| /* There are thirty three bits, the next bit in the sequence is bit-33 XOR |
| * bit-20. The top 1 bit is in u1, the bottom 32 are in u0. |
| */ |
| size_t i; |
| for (i=0; i<size; ++i) |
| { |
| /* First generate 8 new bits then shift them in at the end. */ |
| png_uint_32 u = ((u0 >> (20-8)) ^ ((u1 << 7) | (u0 >> (32-7)))) & 0xff; |
| u1 <<= 8; |
| u1 |= u0 >> 24; |
| u0 <<= 8; |
| u0 |= u; |
| *bytes++ = (png_byte)u; |
| } |
| |
| seed[0] = u0; |
| seed[1] = u1; |
| } |
| |
| static void |
| make_four_random_bytes(png_uint_32* seed, png_bytep bytes) |
| { |
| make_random_bytes(seed, bytes, 4); |
| } |
| |
| #if defined PNG_READ_SUPPORTED || defined PNG_WRITE_tRNS_SUPPORTED |
| static void |
| randomize(void *pv, size_t size) |
| { |
| static png_uint_32 random_seed[2] = {0x56789abc, 0xd}; |
| make_random_bytes(random_seed, pv, size); |
| } |
| |
| #define RANDOMIZE(this) randomize(&(this), sizeof (this)) |
| #endif /* READ || WRITE_tRNS */ |
| |
| #ifdef PNG_READ_TRANSFORMS_SUPPORTED |
| static unsigned int |
| random_mod(unsigned int max) |
| { |
| unsigned int x; |
| |
| RANDOMIZE(x); |
| |
| return x % max; /* 0 .. max-1 */ |
| } |
| |
| #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; |
| } |
| #endif |
| #endif /* PNG_READ_SUPPORTED */ |
| |
| /* 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 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) + \ |
| (((do_interlace)!=0)<<15) + ((width)<<16) + ((height)<<24))) |
| |
| #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) ((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) |
| |
| /* Utility to construct a standard name for a standard image. */ |
| static size_t |
| standard_name(char *buffer, size_t bufsize, size_t pos, png_byte colour_type, |
| int bit_depth, unsigned int npalette, int interlace_type, |
| png_uint_32 w, png_uint_32 h, int do_interlace) |
| { |
| pos = safecat(buffer, bufsize, pos, colour_types[colour_type]); |
| 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"); |
| |
| if (interlace_type != PNG_INTERLACE_NONE) |
| { |
| pos = safecat(buffer, bufsize, pos, " interlaced"); |
| if (do_interlace) |
| pos = safecat(buffer, bufsize, pos, "(pngvalid)"); |
| else |
| pos = safecat(buffer, bufsize, pos, "(libpng)"); |
| } |
| |
| if (w > 0 || h > 0) |
| { |
| pos = safecat(buffer, bufsize, pos, " "); |
| pos = safecatn(buffer, bufsize, pos, w); |
| pos = safecat(buffer, bufsize, pos, "x"); |
| pos = safecatn(buffer, bufsize, pos, h); |
| } |
| |
| return pos; |
| } |
| |
| static size_t |
| standard_name_from_id(char *buffer, size_t bufsize, size_t pos, png_uint_32 id) |
| { |
| return standard_name(buffer, bufsize, pos, COL_FROM_ID(id), |
| DEPTH_FROM_ID(id), PALETTE_FROM_ID(id), INTERLACE_FROM_ID(id), |
| WIDTH_FROM_ID(id), HEIGHT_FROM_ID(id), DO_INTERLACE_FROM_ID(id)); |
| } |
| |
| /* Convenience API and defines to list valid formats. Note that 16 bit read and |
| * write support is required to do 16 bit read tests (we must be able to make a |
| * 16 bit image to test!) |
| */ |
| #ifdef PNG_WRITE_16BIT_SUPPORTED |
| # define WRITE_BDHI 4 |
| # ifdef PNG_READ_16BIT_SUPPORTED |
| # define READ_BDHI 4 |
| # define DO_16BIT |
| # endif |
| #else |
| # define WRITE_BDHI 3 |
| #endif |
| #ifndef DO_16BIT |
| # define READ_BDHI 3 |
| #endif |
| |
| /* The following defines the number of different palettes to generate for |
| * each log bit depth of a colour type 3 standard image. |
| */ |
| #define PALETTE_COUNT(bit_depth) ((bit_depth) > 4 ? 1U : 16U) |
| |
| static int |
| next_format(png_bytep colour_type, png_bytep bit_depth, |
| unsigned int* palette_number, int low_depth_gray, int tRNS) |
| { |
| if (*bit_depth == 0) |
| { |
| *colour_type = 0; |
| if (low_depth_gray) |
| *bit_depth = 1; |
| else |
| *bit_depth = 8; |
| *palette_number = 0; |
| return 1; |
| } |
| |
| if (*colour_type < 4/*no alpha channel*/) |
| { |
| /* 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; |
| } |
| |
| *bit_depth = (png_byte)(*bit_depth << 1); |
| |
| /* Palette images are restricted to 8 bit depth */ |
| if (*bit_depth <= 8 |
| #ifdef DO_16BIT |
| || (*colour_type != 3 && *bit_depth <= 16) |
| #endif |
| ) |
| return 1; |
| |
| /* Move to the next color type, or return 0 at the end. */ |
| switch (*colour_type) |
| { |
| case 0: |
| *colour_type = 2; |
| *bit_depth = 8; |
| return 1; |
| |
| case 2: |
| *colour_type = 3; |
| *bit_depth = 1; |
| return 1; |
| |
| case 3: |
| *colour_type = 4; |
| *bit_depth = 8; |
| return 1; |
| |
| case 4: |
| *colour_type = 6; |
| *bit_depth = 8; |
| return 1; |
| |
| default: |
| return 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, int swap16, int littleendian) |
| { |
| png_uint_32 bit_index, result; |
| |
| /* Find a sample index for the desired sample: */ |
| x *= bit_depth; |
| bit_index = x; |
| |
| if ((colour_type & 1) == 0) /* !palette */ |
| { |
| if (colour_type & 2) |
| bit_index *= 3; |
| |
| if (colour_type & 4) |
| bit_index += x; /* Alpha channel */ |
| |
| /* Multiple channels; select one: */ |
| if (colour_type & (2+4)) |
| bit_index += sample_index * bit_depth; |
| } |
| |
| /* Return the sample from the row as an integer. */ |
| row += bit_index >> 3; |
| result = *row; |
| |
| if (bit_depth == 8) |
| return result; |
| |
| else if (bit_depth > 8) |
| { |
| if (swap16) |
| return (*++row << 8) + result; |
| else |
| return (result << 8) + *++row; |
| } |
| |
| /* 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; |
| |
| if (!littleendian) |
| bit_index = 8-bit_index-bit_depth; |
| |
| return (result >> bit_index) & ((1U<<bit_depth)-1); |
| } |
| #endif /* PNG_READ_TRANSFORMS_SUPPORTED */ |
| |
| /* Copy a single pixel, of a given size, from one buffer to another - |
| * while this is basically bit addressed there is an implicit assumption |
| * that pixels 8 or more bits in size are byte aligned and that pixels |
| * do not otherwise cross byte boundaries. (This is, so far as I know, |
| * universally true in bitmap computer graphics. [JCB 20101212]) |
| * |
| * NOTE: The to and from buffers may be the same. |
| */ |
| static void |
| pixel_copy(png_bytep toBuffer, png_uint_32 toIndex, |
| 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. |
| */ |
| toIndex *= pixelSize; |
| fromIndex *= pixelSize; |
| if (pixelSize < 8) /* Sub-byte */ |
| { |
| /* Mask to select the location of the copied pixel: */ |
| 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 (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)); |
| } |
| else /* One or more bytes */ |
| memmove(toBuffer+(toIndex>>3), fromBuffer+(fromIndex>>3), pixelSize>>3); |
| } |
| |
| #ifdef PNG_READ_SUPPORTED |
| /* Copy a complete row of pixels, taking into account potential partial |
| * bytes at the end. |
| */ |
| static void |
| row_copy(png_bytep toBuffer, png_const_bytep fromBuffer, unsigned int bitWidth, |
| int littleendian) |
| { |
| memcpy(toBuffer, fromBuffer, bitWidth >> 3); |
| |
| if ((bitWidth & 7) != 0) |
| { |
| unsigned int mask; |
| |
| toBuffer += bitWidth >> 3; |
| fromBuffer += bitWidth >> 3; |
| if (littleendian) |
| mask = 0xff << (bitWidth & 7); |
| else |
| mask = 0xff >> (bitWidth & 7); |
| *toBuffer = (png_byte)((*toBuffer & mask) | (*fromBuffer & ~mask)); |
| } |
| } |
| |
| /* Compare pixels - they are assumed to start at the first byte in the |
| * given buffers. |
| */ |
| static int |
| pixel_cmp(png_const_bytep pa, png_const_bytep pb, png_uint_32 bit_width) |
| { |
| #if PNG_LIBPNG_VER < 10506 |
| if (memcmp(pa, pb, bit_width>>3) == 0) |
| { |
| png_uint_32 p; |
| |
| if ((bit_width & 7) == 0) return 0; |
| |
| /* Ok, any differences? */ |
| p = pa[bit_width >> 3]; |
| p ^= pb[bit_width >> 3]; |
| |
| if (p == 0) return 0; |
| |
| /* There are, but they may not be significant, remove the bits |
| * after the end (the low order bits in PNG.) |
| */ |
| bit_width &= 7; |
| p >>= 8-bit_width; |
| |
| if (p == 0) return 0; |
| } |
| #else |
| /* From libpng-1.5.6 the overwrite should be fixed, so compare the trailing |
| * bits too: |
| */ |
| if (memcmp(pa, pb, (bit_width+7)>>3) == 0) |
| return 0; |
| #endif |
| |
| /* Return the index of the changed byte. */ |
| { |
| png_uint_32 where = 0; |
| |
| while (pa[where] == pb[where]) ++where; |
| return 1+where; |
| } |
| } |
| #endif /* PNG_READ_SUPPORTED */ |
| |
| /*************************** BASIC PNG FILE WRITING ***************************/ |
| /* A png_store takes data from the sequential writer or provides data |
| * to the sequential reader. It can also store the result of a PNG |
| * write for later retrieval. |
| */ |
| #define STORE_BUFFER_SIZE 500 /* arbitrary */ |
| typedef struct png_store_buffer |
| { |
| struct png_store_buffer* prev; /* NOTE: stored in reverse order */ |
| png_byte buffer[STORE_BUFFER_SIZE]; |
| } png_store_buffer; |
| |
| #define FILE_NAME_SIZE 64 |
| |
| typedef struct store_palette_entry /* record of a single palette entry */ |
| { |
| png_byte red; |
| png_byte green; |
| png_byte blue; |
| png_byte alpha; |
| } store_palette_entry, store_palette[256]; |
| |
| typedef struct png_store_file |
| { |
| struct png_store_file* next; /* as many as you like... */ |
| char name[FILE_NAME_SIZE]; |
| png_uint_32 id; /* must be correct (see FILEID) */ |
| png_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 */ |
| } png_store_file; |
| |
| /* The following is a pool of memory allocated by a single libpng read or write |
| * operation. |
| */ |
| typedef struct store_pool |
| { |
| struct png_store *store; /* Back pointer */ |
| struct store_memory *list; /* List of allocated memory */ |
| png_byte mark[4]; /* Before and after data */ |
| |
| /* Statistics for this run. */ |
| png_alloc_size_t max; /* Maximum single allocation */ |
| png_alloc_size_t current; /* Current allocation */ |
| png_alloc_size_t limit; /* Highest current allocation */ |
| png_alloc_size_t total; /* Total allocation */ |
| |
| /* Overall statistics (retained across successive runs). */ |
| png_alloc_size_t max_max; |
| png_alloc_size_t max_limit; |
| png_alloc_size_t max_total; |
| } store_pool; |
| |
| typedef struct png_store |
| { |
| /* For cexcept.h exception handling - simply store one of these; |
| * the context is a self pointer but it may point to a different |
| * png_store (in fact it never does in this program.) |
| */ |
| struct exception_context |
| exception_context; |
| |
| unsigned int verbose :1; |
| unsigned int treat_warnings_as_errors :1; |
| unsigned int expect_error :1; |
| unsigned int expect_warning :1; |
| unsigned int saw_warning :1; |
| unsigned int speed :1; |
| unsigned int progressive :1; /* use progressive read */ |
| unsigned int validated :1; /* used as a temporary flag */ |
| int nerrors; |
| int nwarnings; |
| int noptions; /* number of options below: */ |
| struct { |
| unsigned char option; /* option number, 0..30 */ |
| unsigned char setting; /* setting (unset,invalid,on,off) */ |
| } options[16]; |
| char test[128]; /* Name of test */ |
| char error[256]; |
| |
| /* 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 */ |
| 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) */ |
| png_uint_32 image_h; /* Number of rows in a single image */ |
| store_pool read_memory_pool; |
| |
| /* Write fields */ |
| png_store_file* saved; |
| png_structp pwrite; /* Used when writing a new file */ |
| png_infop piwrite; |
| png_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; |
| store_palette_entry* palette; |
| int npalette; |
| } png_store; |
| |
| /* Initialization and cleanup */ |
| static void |
| store_pool_mark(png_bytep mark) |
| { |
| static png_uint_32 store_seed[2] = { 0x12345678, 1}; |
| |
| make_four_random_bytes(store_seed, mark); |
| } |
| |
| #ifdef PNG_READ_TRANSFORMS_SUPPORTED |
| /* Use this for random 32 bit values; this function makes sure the result is |
| * non-zero. |
| */ |
| static png_uint_32 |
| random_32(void) |
| { |
| |
| for (;;) |
| { |
| png_byte mark[4]; |
| png_uint_32 result; |
| |
| store_pool_mark(mark); |
| result = png_get_uint_32(mark); |
| |
| if (result != 0) |
| return result; |
| } |
| } |
| #endif /* PNG_READ_SUPPORTED */ |
| |
| static void |
| store_pool_init(png_store *ps, store_pool *pool) |
| { |
| memset(pool, 0, sizeof *pool); |
| |
| pool->store = ps; |
| pool->list = NULL; |
| pool->max = pool->current = pool->limit = pool->total = 0; |
| pool->max_max = pool->max_limit = pool->max_total = 0; |
| store_pool_mark(pool->mark); |
| } |
| |
| static void |
| store_init(png_store* ps) |
| { |
| memset(ps, 0, sizeof *ps); |
| init_exception_context(&ps->exception_context); |
| store_pool_init(ps, &ps->read_memory_pool); |
| store_pool_init(ps, &ps->write_memory_pool); |
| ps->verbose = 0; |
| ps->treat_warnings_as_errors = 0; |
| ps->expect_error = 0; |
| ps->expect_warning = 0; |
| ps->saw_warning = 0; |
| ps->speed = 0; |
| ps->progressive = 0; |
| ps->validated = 0; |
| ps->nerrors = ps->nwarnings = 0; |
| ps->pread = NULL; |
| ps->piread = NULL; |
| ps->saved = ps->current = NULL; |
| ps->next = NULL; |
| ps->readpos = 0; |
| ps->image = NULL; |
| ps->cb_image = 0; |
| ps->cb_row = 0; |
| ps->image_h = 0; |
| ps->pwrite = NULL; |
| ps->piwrite = NULL; |
| ps->writepos = 0; |
| ps->new.prev = NULL; |
| ps->palette = NULL; |
| ps->npalette = 0; |
| ps->noptions = 0; |
| } |
| |
| static void |
| store_freebuffer(png_store_buffer* psb) |
| { |
| if (psb->prev) |
| { |
| store_freebuffer(psb->prev); |
| free(psb->prev); |
| psb->prev = NULL; |
| } |
| } |
| |
| static void |
| store_freenew(png_store *ps) |
| { |
| store_freebuffer(&ps->new); |
| ps->writepos = 0; |
| if (ps->palette != NULL) |
| { |
| free(ps->palette); |
| ps->palette = NULL; |
| ps->npalette = 0; |
| } |
| } |
| |
| static void |
| store_storenew(png_store *ps) |
| { |
| 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) |
| png_error(ps->pwrite, "store new: OOM"); |
| |
| *pb = ps->new; |
| ps->new.prev = pb; |
| ps->writepos = 0; |
| } |
| |
| static void |
| store_freefile(png_store_file **ppf) |
| { |
| if (*ppf != NULL) |
| { |
| store_freefile(&(*ppf)->next); |
| |
| store_freebuffer(&(*ppf)->data); |
| (*ppf)->datacount = 0; |
| if ((*ppf)->palette != NULL) |
| { |
| free((*ppf)->palette); |
| (*ppf)->palette = NULL; |
| (*ppf)->npalette = 0; |
| } |
| free(*ppf); |
| *ppf = NULL; |
| } |
| } |
| |
| /* Main interface to file storeage, 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)); |
| 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; |
| ps->new.prev = NULL; |
| ps->writepos = 0; |
| pf->palette = ps->palette; |
| pf->npalette = ps->npalette; |
| ps->palette = 0; |
| ps->npalette = 0; |
| |
| /* And save it. */ |
| pf->next = ps->saved; |
| ps->saved = pf; |
| } |
| |
| /* 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, const char *msg) |
| { |
| if (pp != NULL && pp == ps->pread) |
| { |
| /* Reading a file */ |
| pos = safecat(buffer, bufsize, pos, "read: "); |
| |
| if (ps->current != NULL) |
| { |
| pos = safecat(buffer, bufsize, pos, ps->current->name); |
| pos = safecat(buffer, bufsize, pos, sep); |
| } |
| } |
| |
| else if (pp != NULL && pp == ps->pwrite) |
| { |
| /* Writing a file */ |
| pos = safecat(buffer, bufsize, pos, "write: "); |
| pos = safecat(buffer, bufsize, pos, ps->wname); |
| pos = safecat(buffer, bufsize, pos, sep); |
| } |
| |
| else |
| { |
| /* Neither reading nor writing (or a memory error in struct delete) */ |
| pos = safecat(buffer, bufsize, pos, "pngvalid: "); |
| } |
| |
| if (ps->test[0] != 0) |
| { |
| pos = safecat(buffer, bufsize, pos, ps->test); |
| pos = safecat(buffer, bufsize, pos, sep); |
| } |
| pos = safecat(buffer, bufsize, pos, msg); |
| return pos; |
| } |
| |
| /* Verbose output to the error stream: */ |
| static void |
| store_verbose(png_store *ps, png_const_structp pp, png_const_charp prefix, |
| png_const_charp message) |
| { |
| char buffer[512]; |
| |
| if (prefix) |
| fputs(prefix, stderr); |
| |
| (void)store_message(ps, pp, buffer, sizeof buffer, 0, message); |
| fputs(buffer, stderr); |
| fputc('\n', stderr); |
| } |
| |
| /* Log an error or warning - the relevant count is always incremented. */ |
| static void |
| store_log(png_store* ps, png_const_structp pp, png_const_charp message, |
| int is_error) |
| { |
| /* The warning is copied to the error buffer if there are no errors and it is |
| * the first warning. The error is copied to the error buffer if it is the |
| * first error (overwriting any prior warnings). |
| */ |
| if (is_error ? (ps->nerrors)++ == 0 : |
| (ps->nwarnings)++ == 0 && ps->nerrors == 0) |
| store_message(ps, pp, ps->error, sizeof ps->error, 0, message); |
| |
| if (ps->verbose) |
| store_verbose(ps, pp, is_error ? "error: " : "warning: ", message); |
| } |
| |
| #ifdef PNG_READ_SUPPORTED |
| /* Internal error function, called with a png_store but no libpng stuff. */ |
| static void |
| internal_error(png_store *ps, png_const_charp message) |
| { |
| store_log(ps, NULL, message, 1 /* error */); |
| |
| /* And finally throw an exception. */ |
| { |
| struct exception_context *the_exception_context = &ps->exception_context; |
| Throw ps; |
| } |
| } |
| #endif /* PNG_READ_SUPPORTED */ |
| |
| /* Functions to use as PNG callbacks. */ |
| static void PNGCBAPI |
| store_error(png_structp ppIn, png_const_charp message) /* PNG_NORETURN */ |
| { |
| png_const_structp pp = ppIn; |
| png_store *ps = voidcast(png_store*, png_get_error_ptr(pp)); |
| |
| if (!ps->expect_error) |
| store_log(ps, pp, message, 1 /* error */); |
| |
| /* And finally throw an exception. */ |
| { |
| struct exception_context *the_exception_context = &ps->exception_context; |
| Throw ps; |
| } |
| } |
| |
| static void PNGCBAPI |
| store_warning(png_structp ppIn, png_const_charp message) |
| { |
| png_const_structp pp = ppIn; |
| png_store *ps = voidcast(png_store*, png_get_error_ptr(pp)); |
| |
| if (!ps->expect_warning) |
| store_log(ps, pp, message, 0 /* warning */); |
| else |
| ps->saw_warning = 1; |
| } |
| |
| /* These somewhat odd functions are used when reading an image to ensure that |
| * the buffer is big enough, the png_structp is for errors. |
| */ |
| /* Return a single row from the correct image. */ |
| static png_bytep |
| 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; |
| |
| if (ps->image == NULL) |
| png_error(pp, "no allocated image"); |
| |
| if (coffset + ps->cb_row + 3 > ps->cb_image) |
| png_error(pp, "image too small"); |
| |
| return ps->image + coffset; |
| } |
| |
| static void |
| store_image_free(png_store *ps, png_const_structp pp) |
| { |
| if (ps->image != NULL) |
| { |
| png_bytep image = ps->image; |
| |
| if (image[-1] != 0xed || image[ps->cb_image] != 0xfe) |
| { |
| if (pp != NULL) |
| png_error(pp, "png_store image overwrite (1)"); |
| else |
| store_log(ps, NULL, "png_store image overwrite (2)", 1); |
| } |
| |
| ps->image = NULL; |
| ps->cb_image = 0; |
| --image; |
| free(image); |
| } |
| } |
| |
| static void |
| store_ensure_image(png_store *ps, png_const_structp pp, int nImages, |
| png_size_t cbRow, png_uint_32 cRows) |
| { |
| png_size_t cb = nImages * cRows * (cbRow + 5); |
| |
| if (ps->cb_image < cb) |
| { |
| png_bytep image; |
| |
| store_image_free(ps, pp); |
| |
| /* The buffer is deliberately mis-aligned. */ |
| image = voidcast(png_bytep, malloc(cb+2)); |
| if (image == NULL) |
| { |
| /* Called from the startup - ignore the error for the moment. */ |
| if (pp == NULL) |
| return; |
| |
| png_error(pp, "OOM allocating image buffer"); |
| } |
| |
| /* These magic tags are used to detect overwrites above. */ |
| ++image; |
| image[-1] = 0xed; |
| image[cb] = 0xfe; |
| |
| ps->image = image; |
| ps->cb_image = cb; |
| } |
| |
| /* We have an adequate sized image; lay out the rows. There are 2 bytes at |
| * the start and three at the end of each (this ensures that the row |
| * alignment starts out odd - 2+1 and changes for larger images on each row.) |
| */ |
| ps->cb_row = cbRow; |
| ps->image_h = cRows; |
| |
| /* For error checking, the whole buffer is set to 10110010 (0xb2 - 178). |
| * This deliberately doesn't match the bits in the size test image which are |
| * outside the image; these are set to 0xff (all 1). To make the row |
| * comparison work in the 'size' test case the size rows are pre-initialized |
| * to the same value prior to calling 'standard_row'. |
| */ |
| memset(ps->image, 178, cb); |
| |
| /* Then put in the marks. */ |
| while (--nImages >= 0) |
| { |
| png_uint_32 y; |
| |
| for (y=0; y<cRows; ++y) |
| { |
| png_bytep row = store_image_row(ps, pp, nImages, y); |
| |
| /* The markers: */ |
| row[-2] = 190; |
| row[-1] = 239; |
| row[cbRow] = 222; |
| row[cbRow+1] = 173; |
| row[cbRow+2] = 17; |
| } |
| } |
| } |
| |
| #ifdef PNG_READ_SUPPORTED |
| static void |
| store_image_check(const png_store* ps, png_const_structp pp, int iImage) |
| { |
| png_const_bytep image = ps->image; |
| |
| if (image[-1] != 0xed || image[ps->cb_image] != 0xfe) |
| png_error(pp, "image overwrite"); |
| else |
| { |
| png_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) |
| { |
| if (image[-2] != 190 || image[-1] != 239) |
| png_error(pp, "row start overwritten"); |
| |
| if (image[cbRow] != 222 || image[cbRow+1] != 173 || |
| image[cbRow+2] != 17) |
| png_error(pp, "row end overwritten"); |
| |
| image += cbRow+5; |
| } |
| } |
| } |
| #endif /* PNG_READ_SUPPORTED */ |
| |
| static void PNGCBAPI |
| store_write(png_structp ppIn, png_bytep pb, png_size_t st) |
| { |
| png_const_structp pp = ppIn; |
| png_store *ps = voidcast(png_store*, png_get_io_ptr(pp)); |
| |
| if (ps->pwrite != pp) |
| png_error(pp, "store state damaged"); |
| |
| while (st > 0) |
| { |
| size_t cb; |
| |
| if (ps->writepos >= STORE_BUFFER_SIZE) |
| store_storenew(ps); |
| |
| cb = st; |
| |
| if (cb > STORE_BUFFER_SIZE - ps->writepos) |
| cb = STORE_BUFFER_SIZE - ps->writepos; |
| |
| memcpy(ps->new.buffer + ps->writepos, pb, cb); |
| pb += cb; |
| st -= cb; |
| ps->writepos += cb; |
| } |
| } |
| |
| static void PNGCBAPI |
| store_flush(png_structp ppIn) |
| { |
| UNUSED(ppIn) /*DOES NOTHING*/ |
| } |
| |
| #ifdef PNG_READ_SUPPORTED |
| static size_t |
| store_read_buffer_size(png_store *ps) |
| { |
| /* Return the bytes available for read in the current buffer. */ |
| if (ps->next != &ps->current->data) |
| return STORE_BUFFER_SIZE; |
| |
| 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) |
| { |
| if (ps->current != NULL && ps->next != NULL) |
| { |
| png_store_buffer *next = &ps->current->data; |
| size_t cbAvail = ps->current->datacount; |
| |
| while (next != ps->next && next != NULL) |
| { |
| next = next->prev; |
| cbAvail += STORE_BUFFER_SIZE; |
| } |
| |
| if (next != ps->next) |
| png_error(ps->pread, "buffer read error"); |
| |
| if (cbAvail > ps->readpos) |
| return cbAvail - ps->readpos; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static int |
| store_read_buffer_next(png_store *ps) |
| { |
| png_store_buffer *pbOld = ps->next; |
| png_store_buffer *pbNew = &ps->current->data; |
| if (pbOld != pbNew) |
| { |
| while (pbNew != NULL && pbNew->prev != pbOld) |
| pbNew = pbNew->prev; |
| |
| if (pbNew != NULL) |
| { |
| ps->next = pbNew; |
| ps->readpos = 0; |
| return 1; |
| } |
| |
| png_error(ps->pread, "buffer lost"); |
| } |
| |
| return 0; /* EOF or error */ |
| } |
| |
| /* Need separate implementation and callback to allow use of the same code |
| * 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) |
| { |
| if (ps->current == NULL || ps->next == NULL) |
| png_error(ps->pread, "store state damaged"); |
| |
| while (st > 0) |
| { |
| size_t cbAvail = store_read_buffer_size(ps) - ps->readpos; |
| |
| if (cbAvail > 0) |
| { |
| if (cbAvail > st) cbAvail = st; |
| memcpy(pb, ps->next->buffer + ps->readpos, cbAvail); |
| st -= cbAvail; |
| pb += cbAvail; |
| ps->readpos += cbAvail; |
| } |
| |
| else if (!store_read_buffer_next(ps)) |
| png_error(ps->pread, "read beyond end of file"); |
| } |
| } |
| |
| static void PNGCBAPI |
| store_read(png_structp ppIn, png_bytep pb, png_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); |
| } |
| |
| 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 |
| { |
| if (ps->readpos != 0) |
| png_error(pp, "store_read called during progressive read"); |
| |
| png_process_data(pp, pi, ps->next->buffer, store_read_buffer_size(ps)); |
| } |
| while (store_read_buffer_next(ps)); |
| } |
| #endif /* PNG_READ_SUPPORTED */ |
| |
| /* The caller must fill this in: */ |
| static store_palette_entry * |
| store_write_palette(png_store *ps, int npalette) |
| { |
| if (ps->pwrite == NULL) |
| store_log(ps, NULL, "attempt to write palette without write stream", 1); |
| |
| if (ps->palette != NULL) |
| png_error(ps->pwrite, "multiple store_write_palette calls"); |
| |
| /* This function can only return NULL if called with '0'! */ |
| if (npalette > 0) |
| { |
| ps->palette = voidcast(store_palette_entry*, malloc(npalette * |
| sizeof *ps->palette)); |
| |
| if (ps->palette == NULL) |
| png_error(ps->pwrite, "store new palette: OOM"); |
| |
| ps->npalette = npalette; |
| } |
| |
| return ps->palette; |
| } |
| |
| #ifdef PNG_READ_SUPPORTED |
| static store_palette_entry * |
| store_current_palette(png_store *ps, int *npalette) |
| { |
| /* This is an internal error (the call has been made outside a read |
| * 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; |
| return ps->current->palette; |
| } |
| #endif /* PNG_READ_SUPPORTED */ |
| |
| /***************************** MEMORY MANAGEMENT*** ***************************/ |
| #ifdef PNG_USER_MEM_SUPPORTED |
| /* A store_memory is simply the header for an allocated block of memory. The |
| * pointer returned to libpng is just after the end of the header block, the |
| * allocated memory is followed by a second copy of the 'mark'. |
| */ |
| typedef struct store_memory |
| { |
| store_pool *pool; /* Originating pool */ |
| struct store_memory *next; /* Singly linked list */ |
| png_alloc_size_t size; /* Size of memory allocated */ |
| png_byte mark[4]; /* ID marker */ |
| } store_memory; |
| |
| /* Handle a fatal error in memory allocation. This calls png_error if the |
| * libpng struct is non-NULL, else it outputs a message and returns. This means |
| * that a memory problem while libpng is running will abort (png_error) the |
| * handling of particular file while one in cleanup (after the destroy of the |
| * struct has returned) will simply keep going and free (or attempt to free) |
| * all the memory. |
| */ |
| static void |
| store_pool_error(png_store *ps, png_const_structp pp, const char *msg) |
| { |
| if (pp != NULL) |
| png_error(pp, msg); |
| |
| /* Else we have to do it ourselves. png_error eventually calls store_log, |
| * above. store_log accepts a NULL png_structp - it just changes what gets |
| * output by store_message. |
| */ |
| store_log(ps, pp, msg, 1 /* error */); |
| } |
| |
| static void |
| store_memory_free(png_const_structp pp, store_pool *pool, store_memory *memory) |
| { |
| /* Note that pp may be NULL (see store_pool_delete below), the caller has |
| * found 'memory' in pool->list *and* unlinked this entry, so this is a valid |
| * pointer (for sure), but the contents may have been trashed. |
| */ |
| if (memory->pool != pool) |
| store_pool_error(pool->store, pp, "memory corrupted (pool)"); |
| |
| else if (memcmp(memory->mark, pool->mark, sizeof memory->mark) != 0) |
| store_pool_error(pool->store, pp, "memory corrupted (start)"); |
| |
| /* It should be safe to read the size field now. */ |
| else |
| { |
| png_alloc_size_t cb = memory->size; |
| |
| if (cb > pool->max) |
| store_pool_error(pool->store, pp, "memory corrupted (size)"); |
| |
| else if (memcmp((png_bytep)(memory+1)+cb, pool->mark, sizeof pool->mark) |
| != 0) |
| store_pool_error(pool->store, pp, "memory corrupted (end)"); |
| |
| /* Finally give the library a chance to find problems too: */ |
| else |
| { |
| pool->current -= cb; |
| free(memory); |
| } |
| } |
| } |
| |
| static void |
| store_pool_delete(png_store *ps, store_pool *pool) |
| { |
| if (pool->list != NULL) |
| { |
| fprintf(stderr, "%s: %s %s: memory lost (list follows):\n", ps->test, |
| pool == &ps->read_memory_pool ? "read" : "write", |
| pool == &ps->read_memory_pool ? (ps->current != NULL ? |
| ps->current->name : "unknown file") : ps->wname); |
| ++ps->nerrors; |
| |
| do |
| { |
| store_memory *next = pool->list; |
| pool->list = next->next; |
| next->next = NULL; |
| |
| fprintf(stderr, "\t%lu bytes @ %p\n", |
| (unsigned long)next->size, (const void*)(next+1)); |
| /* The NULL means this will always return, even if the memory is |
| * corrupted. |
| */ |
| store_memory_free(NULL, pool, next); |
| } |
| while (pool->list != NULL); |
| } |
| |
| /* And reset the other fields too for the next time. */ |
| if (pool->max > pool->max_max) pool->max_max = pool->max; |
| pool->max = 0; |
| if (pool->current != 0) /* unexpected internal error */ |
| fprintf(stderr, "%s: %s %s: memory counter mismatch (internal error)\n", |
| ps->test, pool == &ps->read_memory_pool ? "read" : "write", |
| pool == &ps->read_memory_pool ? (ps->current != NULL ? |
| ps->current->name : "unknown file") : ps->wname); |
| pool->current = 0; |
| |
| if (pool->limit > pool->max_limit) |
| pool->max_limit = pool->limit; |
| |
| pool->limit = 0; |
| |
| if (pool->total > pool->max_total) |
| pool->max_total = pool->total; |
| |
| pool->total = 0; |
| |
| /* Get a new mark too. */ |
| store_pool_mark(pool->mark); |
| } |
| |
| /* The memory callbacks: */ |
| static png_voidp PNGCBAPI |
| store_malloc(png_structp ppIn, png_alloc_size_t cb) |
| { |
| png_const_structp pp = ppIn; |
| store_pool *pool = voidcast(store_pool*, png_get_mem_ptr(pp)); |
| store_memory *new = voidcast(store_memory*, malloc(cb + (sizeof *new) + |
| (sizeof pool->mark))); |
| |
| if (new != NULL) |
| { |
| if (cb > pool->max) |
| pool->max = cb; |
| |
| pool->current += cb; |
| |
| if (pool->current > pool->limit) |
| pool->limit = pool->current; |
| |
| pool->total += cb; |
| |
| new->size = cb; |
| memcpy(new->mark, pool->mark, sizeof new->mark); |
| memcpy((png_byte*)(new+1) + cb, pool->mark, sizeof pool->mark); |
| new->pool = pool; |
| new->next = pool->list; |
| pool->list = new; |
| ++new; |
| } |
| |
| else |
| { |
| /* NOTE: the PNG user malloc function cannot use the png_ptr it is passed |
| * other than to retrieve the allocation pointer! libpng calls the |
| * store_malloc callback in two basic cases: |
| * |
| * 1) From png_malloc; png_malloc will do a png_error itself if NULL is |
| * returned. |
| * 2) From png_struct or png_info structure creation; png_malloc is |
| * to return so cleanup can be performed. |
| * |
| * To handle this store_malloc can log a message, but can't do anything |
| * else. |
| */ |
| store_log(pool->store, pp, "out of memory", 1 /* is_error */); |
| } |
| |
| return new; |
| } |
| |
| static void PNGCBAPI |
| store_free(png_structp ppIn, png_voidp memory) |
| { |
| png_const_structp pp = ppIn; |
| store_pool *pool = voidcast(store_pool*, png_get_mem_ptr(pp)); |
| store_memory *this = voidcast(store_memory*, memory), **test; |
| |
| /* Because libpng calls store_free with a dummy png_struct when deleting |
| * png_struct or png_info via png_destroy_struct_2 it is necessary to check |
| * the passed in png_structp to ensure it is valid, and not pass it to |
| * png_error if it is not. |
| */ |
| if (pp != pool->store->pread && pp != pool->store->pwrite) |
| pp = NULL; |
| |
| /* First check that this 'memory' really is valid memory - it must be in the |
| * pool list. If it is, use the shared memory_free function to free it. |
| */ |
| --this; |
| for (test = &pool->list; *test != this; test = &(*test)->next) |
| { |
| if (*test == NULL) |
| { |
| store_pool_error(pool->store, pp, "bad pointer to free"); |
| return; |
| } |
| } |
| |
| /* Unlink this entry, *test == this. */ |
| *test = this->next; |
| this->next = NULL; |
| store_memory_free(pp, pool, this); |
| } |
| #endif /* PNG_USER_MEM_SUPPORTED */ |
| |
| /* Setup functions. */ |
| /* Cleanup when aborting a write or after storing the new file. */ |
| static void |
| store_write_reset(png_store *ps) |
| { |
| if (ps->pwrite != NULL) |
| { |
| anon_context(ps); |
| |
| Try |
| png_destroy_write_struct(&ps->pwrite, &ps->piwrite); |
| |
| Catch_anonymous |
| { |
| /* memory corruption: continue. */ |
| } |
| |
| ps->pwrite = NULL; |
| ps->piwrite = NULL; |
| } |
| |
| /* And make sure that all the memory has been freed - this will output |
| * spurious errors in the case of memory corruption above, but this is safe. |
| */ |
| # ifdef PNG_USER_MEM_SUPPORTED |
| store_pool_delete(ps, &ps->write_memory_pool); |
| # endif |
| |
| store_freenew(ps); |
| } |
| |
| /* The following is the main write function, it returns a png_struct and, |
| * optionally, a png_info suitable for writiing a new PNG file. Use |
| * store_storefile above to record this file after it has been written. The |
| * returned libpng structures as destroyed by store_write_reset above. |
| */ |
| static png_structp |
| set_store_for_write(png_store *ps, png_infopp ppi, const char *name) |
| { |
| anon_context(ps); |
| |
| Try |
| { |
| if (ps->pwrite != NULL) |
| png_error(ps->pwrite, "write store already in use"); |
| |
| store_write_reset(ps); |
| safecat(ps->wname, sizeof ps->wname, 0, name); |
| |
| /* Don't do the slow memory checks if doing a speed test, also if user |
| * memory is not supported we can't do it anyway. |
| */ |
| # ifdef PNG_USER_MEM_SUPPORTED |
| if (!ps->speed) |
| ps->pwrite = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, |
| ps, store_error, store_warning, &ps->write_memory_pool, |
| store_malloc, store_free); |
| |
| else |
| # endif |
| ps->pwrite = png_create_write_struct(PNG_LIBPNG_VER_STRING, |
| ps, store_error, store_warning); |
| |
| png_set_write_fn(ps->pwrite, ps, store_write, store_flush); |
| |
| # ifdef PNG_SET_OPTION_SUPPORTED |
| { |
| int opt; |
| for (opt=0; opt<ps->noptions; ++opt) |
| if (png_set_option(ps->pwrite, ps->options[opt].option, |
| ps->options[opt].setting) == PNG_OPTION_INVALID) |
| png_error(ps->pwrite, "png option invalid"); |
| } |
| # endif |
| |
| if (ppi != NULL) |
| *ppi = ps->piwrite = png_create_info_struct(ps->pwrite); |
| } |
| |
| Catch_anonymous |
| return NULL; |
| |
| return ps->pwrite; |
| } |
| |
| /* Cleanup when finished reading (either due to error or in the success case). |
| * This routine exists even when there is no read support to make the code |
| * tidier (avoid a mass of ifdefs) and so easier to maintain. |
| */ |
| static void |
| store_read_reset(png_store *ps) |
| { |
| # ifdef PNG_READ_SUPPORTED |
| if (ps->pread != NULL) |
| { |
| anon_context(ps); |
| |
| Try |
| png_destroy_read_struct(&ps->pread, &ps->piread, NULL); |
| |
| Catch_anonymous |
| { |
| /* error already output: continue */ |
| } |
| |
| ps->pread = NULL; |
| ps->piread = NULL; |
| } |
| # endif |
| |
| # ifdef PNG_USER_MEM_SUPPORTED |
| /* Always do this to be safe. */ |
| store_pool_delete(ps, &ps->read_memory_pool); |
| # endif |
| |
| ps->current = NULL; |
| ps->next = NULL; |
| ps->readpos = 0; |
| ps->validated = 0; |
| } |
| |
| #ifdef PNG_READ_SUPPORTED |
| static void |
| store_read_set(png_store *ps, png_uint_32 id) |
| { |
| png_store_file *pf = ps->saved; |
| |
| while (pf != NULL) |
| { |
| if (pf->id == id) |
| { |
| ps->current = pf; |
| ps->next = NULL; |
| store_read_buffer_next(ps); |
| return; |
| } |
| |
| pf = pf->next; |
| } |
| |
| { |
| size_t pos; |
| char msg[FILE_NAME_SIZE+64]; |
| |
| pos = standard_name_from_id(msg, sizeof msg, 0, id); |
| pos = safecat(msg, sizeof msg, pos, ": file not found"); |
| png_error(ps->pread, msg); |
| } |
| } |
| |
| /* The main interface for reading a saved file - pass the id number of the file |
| * to retrieve. Ids must be unique or the earlier file will be hidden. The API |
| * returns a png_struct and, optionally, a png_info. Both of these will be |
| * destroyed by store_read_reset above. |
| */ |
| static png_structp |
| set_store_for_read(png_store *ps, png_infopp ppi, png_uint_32 id, |
| const char *name) |
| { |
| /* Set the name for png_error */ |
| safecat(ps->test, sizeof ps->test, 0, name); |
| |
| if (ps->pread != NULL) |
| png_error(ps->pread, "read store already in use"); |
| |
| store_read_reset(ps); |
| |
| /* Both the create APIs can return NULL if used in their default mode |
| * (because there is no other way of handling an error because the jmp_buf |
| * by default is stored in png_struct and that has not been allocated!) |
| * However, given that store_error works correctly in these circumstances |
| * we don't ever expect NULL in this program. |
| */ |
| # ifdef PNG_USER_MEM_SUPPORTED |
| if (!ps->speed) |
| ps->pread = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, ps, |
| store_error, store_warning, &ps->read_memory_pool, store_malloc, |
| store_free); |
| |
| else |
| # endif |
| ps->pread = png_create_read_struct(PNG_LIBPNG_VER_STRING, ps, store_error, |
| store_warning); |
| |
| if (ps->pread == NULL) |
| { |
| struct exception_context *the_exception_context = &ps->exception_context; |
| |
| store_log(ps, NULL, "png_create_read_struct returned NULL (unexpected)", |
| 1 /*error*/); |
| |
| Throw ps; |
| } |
| |
| # ifdef PNG_SET_OPTION_SUPPORTED |
| { |
| int opt; |
| for (opt=0; opt<ps->noptions; ++opt) |
| if (png_set_option(ps->pread, ps->options[opt].option, |
| ps->options[opt].setting) == PNG_OPTION_INVALID) |
| png_error(ps->pread, "png option invalid"); |
| } |
| # endif |
| |
| store_read_set(ps, id); |
| |
| if (ppi != NULL) |
| *ppi = ps->piread = png_create_info_struct(ps->pread); |
| |
| return ps->pread; |
| } |
| #endif /* PNG_READ_SUPPORTED */ |
| |
| /* The overall cleanup of a store simply calls the above then removes all the |
| * saved files. This does not delete the store itself. |
| */ |
| static void |
| store_delete(png_store *ps) |
| { |
| store_write_reset(ps); |
| store_read_reset(ps); |
| store_freefile(&ps->saved); |
| store_image_free(ps, NULL); |
| } |
| |
| /*********************** PNG FILE MODIFICATION ON READ ************************/ |
| /* Files may be modified on read. The following structure contains a complete |
| * png_store together with extra members to handle modification and a special |
| * read callback for libpng. To use this the 'modifications' field must be set |
| * to a list of png_modification structures that actually perform the |
| * modification, otherwise a png_modifier is functionally equivalent to a |
| * png_store. There is a special read function, set_modifier_for_read, which |
| * replaces set_store_for_read. |
| */ |
| typedef enum modifier_state |
| { |
| modifier_start, /* Initial value */ |
| modifier_signature, /* Have a signature */ |
| modifier_IHDR /* Have an IHDR */ |
| } modifier_state; |
| |
| typedef struct CIE_color |
| { |
| /* A single CIE tristimulus value, representing the unique response of a |
| * standard observer to a variety of light spectra. The observer recognizes |
| * all spectra that produce this response as the same color, therefore this |
| * is effectively a description of a color. |
| */ |
| double X, Y, Z; |
| } CIE_color; |
| |
| typedef struct color_encoding |
| { |
| /* A description of an (R,G,B) encoding of color (as defined above); this |
| * includes the actual colors of the (R,G,B) triples (1,0,0), (0,1,0) and |
| * (0,0,1) plus an encoding value that is used to encode the linear |
| * components R, G and B to give the actual values R^gamma, G^gamma and |
| * B^gamma that are stored. |
| */ |
| double gamma; /* Encoding (file) gamma of space */ |
| CIE_color red, green, blue; /* End points */ |
| } color_encoding; |
| |
| #ifdef PNG_READ_SUPPORTED |
| #if defined PNG_READ_TRANSFORMS_SUPPORTED && defined PNG_READ_cHRM_SUPPORTED |
| static double |
| chromaticity_x(CIE_color c) |
| { |
| return c.X / (c.X + c.Y + c.Z); |
| } |
| |
| static double |
| chromaticity_y(CIE_color c) |
| { |
| return c.Y / (c.X + c.Y + c.Z); |
| } |
| |
| static CIE_color |
| white_point(const color_encoding *encoding) |
| { |
| CIE_color white; |
| |
| white.X = encoding->red.X + encoding->green.X + encoding->blue.X; |
| white.Y = encoding->red.Y + encoding->green.Y + encoding->blue.Y; |
| white.Z = encoding->red.Z + encoding->green.Z + encoding->blue.Z; |
| |
| return white; |
| } |
| #endif /* READ_TRANSFORMS && READ_cHRM */ |
| |
| #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED |
| static void |
| normalize_color_encoding(color_encoding *encoding) |
| { |
| const double whiteY = encoding->red.Y + encoding->green.Y + |
| encoding->blue.Y; |
| |
| if (whiteY != 1) |
| { |
| encoding->red.X /= whiteY; |
| encoding->red.Y /= whiteY; |
| encoding->red.Z /= whiteY; |
| encoding->green.X /= whiteY; |
| encoding->green.Y /= whiteY; |
| encoding->green.Z /= whiteY; |
| encoding->blue.X /= whiteY; |
| encoding->blue.Y /= whiteY; |
| encoding->blue.Z /= whiteY; |
| } |
| } |
| #endif |
| |
| #ifdef PNG_READ_TRANSFORMS_SUPPORTED |
| static size_t |
| safecat_color_encoding(char *buffer, size_t bufsize, size_t pos, |
| const color_encoding *e, double encoding_gamma) |
| { |
| if (e != 0) |
| { |
| if (encoding_gamma != 0) |
| pos = safecat(buffer, bufsize, pos, "("); |
| pos = safecat(buffer, bufsize, pos, "R("); |
| pos = safecatd(buffer, bufsize, pos, e->red.X, 4); |
| pos = safecat(buffer, bufsize, pos, ","); |
| pos = safecatd(buffer, bufsize, pos, e->red.Y, 4); |
| pos = safecat(buffer, bufsize, pos, ","); |
| pos = safecatd(buffer, bufsize, pos, e->red.Z, 4); |
| pos = safecat(buffer, bufsize, pos, "),G("); |
| pos = safecatd(buffer, bufsize, pos, e->green.X, 4); |
| pos = safecat(buffer, bufsize, pos, ","); |
| pos = safecatd(buffer, bufsize, pos, e->green.Y, 4); |
| pos = safecat(buffer, bufsize, pos, ","); |
| pos = safecatd(buffer, bufsize, pos, e->green.Z, 4); |
| pos = safecat(buffer, bufsize, pos, "),B("); |
| pos = safecatd(buffer, bufsize, pos, e->blue.X, 4); |
| pos = safecat(buffer, bufsize, pos, ","); |
| pos = safecatd(buffer, bufsize, pos, e->blue.Y, 4); |
| pos = safecat(buffer, bufsize, pos, ","); |
| pos = safecatd(buffer, bufsize, pos, e->blue.Z, 4); |
| pos = safecat(buffer, bufsize, pos, ")"); |
| if (encoding_gamma != 0) |
| pos = safecat(buffer, bufsize, pos, ")"); |
| } |
| |
| if (encoding_gamma != 0) |
| { |
| pos = safecat(buffer, bufsize, pos, "^"); |
| pos = safecatd(buffer, bufsize, pos, encoding_gamma, 5); |
| } |
| |
| return pos; |
| } |
| #endif /* READ_TRANSFORMS */ |
| #endif /* PNG_READ_SUPPORTED */ |
| |
| typedef struct png_modifier |
| { |
| png_store this; /* I am a png_store */ |
| struct png_modification *modifications; /* Changes to make */ |
| |
| modifier_state state; /* My state */ |
| |
| /* Information from IHDR: */ |
| png_byte bit_depth; /* From IHDR */ |
| png_byte colour_type; /* From IHDR */ |
| |
| /* While handling PLTE, IDAT and IEND these chunks may be pended to allow |
| * other chunks to be inserted. |
| */ |
| png_uint_32 pending_len; |
| png_uint_32 pending_chunk; |
| |
| /* Test values */ |
| double *gammas; |
| unsigned int ngammas; |
| unsigned int ngamma_tests; /* Number of gamma tests to run*/ |
| double current_gamma; /* 0 if not set */ |
| const color_encoding *encodings; |
| unsigned int nencodings; |
| const color_encoding *current_encoding; /* If an encoding has been set */ |
| unsigned int encoding_counter; /* For iteration */ |
| int encoding_ignored; /* Something overwrote it */ |
| |
| /* Control variables used to iterate through possible encodings, the |
| * following must be set to 0 and tested by the function that uses the |
| * png_modifier because the modifier only sets it to 1 (true.) |
| */ |
| unsigned int repeat :1; /* Repeat this transform test. */ |
| unsigned int test_uses_encoding :1; |
| |
| /* 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 |
| * below. |
| */ |
| double maxout8; /* Maximum output value error */ |
| double maxabs8; /* Absolute sample error 0..1 */ |
| double maxcalc8; /* Absolute sample error 0..1 */ |
| double maxpc8; /* Percentage sample error 0..100% */ |
| double maxout16; /* Maximum output value error */ |
| double maxabs16; /* Absolute sample error 0..1 */ |
| double maxcalc16;/* Absolute sample error 0..1 */ |
| double maxcalcG; /* Absolute sample error 0..1 */ |
| double maxpc16; /* Percentage sample error 0..100% */ |
| |
| /* This is set by transforms that need to allow a higher limit, it is an |
| * 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. |
| */ |
| double limit; /* limit on error values, normally 4E-3 */ |
| |
| /* Log limits - values above this are logged, but not necessarily |
| * warned. |
| */ |
| double log8; /* Absolute error in 8 bits to log */ |
| double log16; /* Absolute error in 16 bits to log */ |
| |
| /* Logged 8 and 16 bit errors ('output' values): */ |
| double error_gray_2; |
| double error_gray_4; |
| double error_gray_8; |
| double error_gray_16; |
| double error_color_8; |
| double error_color_16; |
| double error_indexed; |
| |
| /* Flags: */ |
| /* Whether to call png_read_update_info, not png_read_start_image, and how |
| * many times to call it. |
| */ |
| int use_update_info; |
| |
| /* Whether or not to interlace. */ |
| int interlace_type :9; /* int, but must store '1' */ |
| |
| /* Run the standard tests? */ |
| unsigned int test_standard :1; |
| |
| /* Run the odd-sized image and interlace read/write tests? */ |
| unsigned int test_size :1; |
| |
| /* 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 |
| * range input-.5 to input+.5 will be accepted, otherwise the value must be |
| * within the normal limits. It should not be necessary to set this; the |
| * result should always be exact within the permitted error limits. |
| */ |
| unsigned int use_input_precision :1; |
| unsigned int use_input_precision_sbit :1; |
| unsigned int use_input_precision_16to8 :1; |
| |
| /* If set assume that the calculation bit depth is set by the input |
| * precision, not the output precision. |
| */ |
| unsigned int calculations_use_input_precision :1; |
| |
| /* If set assume that the calculations are done in 16 bits even if the sample |
| * depth is 8 bits. |
| */ |
| unsigned int assume_16_bit_calculations :1; |
| |
| /* Which gamma tests to run: */ |
| unsigned int test_gamma_threshold :1; |
| unsigned int test_gamma_transform :1; /* main tests */ |
| unsigned int test_gamma_sbit :1; |
| unsigned int test_gamma_scale16 :1; |
| unsigned int test_gamma_background :1; |
| unsigned int test_gamma_alpha_mode :1; |
| 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 |
| * be modified - they must fit (including header and CRC) into the buffer! |
| */ |
| size_t flush; /* Count of bytes to flush */ |
| size_t buffer_count; /* Bytes in buffer */ |
| size_t buffer_position; /* Position in buffer */ |
| png_byte buffer[1024]; |
| } png_modifier; |
| |
| /* This returns true if the test should be stopped now because it has already |
| * failed and it is running silently. |
| */ |
| static int fail(png_modifier *pm) |
| { |
| return !pm->log && !pm->this.verbose && (pm->this.nerrors > 0 || |
| (pm->this.treat_warnings_as_errors && pm->this.nwarnings > 0)); |
| } |
| |
| static void |
| modifier_init(png_modifier *pm) |
| { |
| memset(pm, 0, sizeof *pm); |
| store_init(&pm->this); |
| pm->modifications = NULL; |
| pm->state = modifier_start; |
| pm->sbitlow = 1U; |
| pm->ngammas = 0; |
| pm->ngamma_tests = 0; |
| pm->gammas = 0; |
| pm->current_gamma = 0; |
| pm->encodings = 0; |
| pm->nencodings = 0; |
| pm->current_encoding = 0; |
| pm->encoding_counter = 0; |
| pm->encoding_ignored = 0; |
| pm->repeat = 0; |
| pm->test_uses_encoding = 0; |
| pm->maxout8 = pm->maxpc8 = pm->maxabs8 = pm->maxcalc8 = 0; |
| pm->maxout16 = pm->maxpc16 = pm->maxabs16 = pm->maxcalc16 = 0; |
| pm->maxcalcG = 0; |
| pm->limit = 4E-3; |
| pm->log8 = pm->log16 = 0; /* Means 'off' */ |
| pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0; |
| pm->error_gray_16 = pm->error_color_8 = pm->error_color_16 = 0; |
| pm->error_indexed = 0; |
| pm->use_update_info = 0; |
| pm->interlace_type = PNG_INTERLACE_NONE; |
| 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->calculations_use_input_precision = 0; |
| pm->assume_16_bit_calculations = 0; |
| pm->test_gamma_threshold = 0; |
| pm->test_gamma_transform = 0; |
| pm->test_gamma_sbit = 0; |
| pm->test_gamma_scale16 = 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; |
| |
| /* Rely on the memset for all the other fields - there are no pointers */ |
| } |
| |
| #ifdef PNG_READ_TRANSFORMS_SUPPORTED |
| |
| /* This controls use of checks that explicitly know how libpng digitizes the |
| * samples in calculations; setting this circumvents simple error limit checking |
| * in the rgb_to_gray check, replacing it with an exact copy of the libpng 1.5 |
| * algorithm. |
| */ |
| #define DIGITIZE PNG_LIBPNG_VER < 10700 |
| |
| /* If pm->calculations_use_input_precision is set then operations will happen |
| * with the precision of the input, not the precision of the output depth. |
| * |
| * If pm->assume_16_bit_calculations is set then even 8 bit calculations use 16 |
| * bit precision. This only affects those of the following limits that pertain |
| * to a calculation - not a digitization operation - unless the following API is |
| * called directly. |
| */ |
| #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED |
| #if DIGITIZE |
| static double digitize(double value, int depth, int do_round) |
| { |
| /* 'value' is in the range 0 to 1, the result is the same value rounded to a |
| * multiple of the digitization factor - 8 or 16 bits depending on both the |
| * sample depth and the 'assume' setting. Digitization is normally by |
| * rounding and 'do_round' should be 1, if it is 0 the digitized value will |
| * be truncated. |
| */ |
| const 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. |
| */ |
| if (value <= 0) |
| value = 0; |
| |
| else if (value >= 1) |
| value = 1; |
| |
| value *= digitization_factor; |
| if (do_round) value += .5; |
| return floor(value)/digitization_factor; |
| } |
| #endif |
| #endif /* RGB_TO_GRAY */ |
| |
| #ifdef PNG_READ_GAMMA_SUPPORTED |
| 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. |
| */ |
| if (pm->assume_16_bit_calculations || |
| (pm->calculations_use_input_precision ? in_depth : out_depth) == 16) |
| return pm->maxabs16; |
| else |
| return pm->maxabs8; |
| } |
| |
| 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). |
| */ |
| if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16) |
| return pm->maxcalc16; |
| else if (pm->assume_16_bit_calculations) |
| return pm->maxcalcG; |
| else |
| return pm->maxcalc8; |
| } |
| |
| 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. |
| */ |
| if (pm->assume_16_bit_calculations || |
| (pm->calculations_use_input_precision ? in_depth : out_depth) == 16) |
| return pm->maxpc16 * .01; |
| else |
| return pm->maxpc8 * .01; |
| } |
| |
| /* Output error - the error in the encoded value. This is determined by the |
| * digitization of the output so can be +/-0.5 in the actual output value. In |
| * the expand_16 case with the current code in libpng the expand happens after |
| * all the calculations are done in 8 bit arithmetic, so even though the output |
| * depth is 16 the output error is determined by the 8 bit calculation. |
| * |
| * This limit is not determined by the bit depth of internal calculations. |
| * |
| * The specified parameter does *not* include the base .5 digitization error but |
| * it is added here. |
| */ |
| 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 |
| * error in 4 bit grayscale gamma is up to the value below. This is a hack |
| * to allow pngvalid to succeed: |
| * |
| * TODO: fix this in libpng |
| */ |
| if (out_depth == 2) |
| return .73182-.5; |
| |
| if (out_depth == 4) |
| return .90644-.5; |
| |
| if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16) |
| return pm->maxout16; |
| |
| /* This is the case where the value was calculated at 8-bit precision then |
| * scaled to 16 bits. |
| */ |
| else if (out_depth == 16) |
| return pm->maxout8 * 257; |
| |
| else |
| return pm->maxout8; |
| } |
| |
| /* This does the same thing as the above however it returns the value to log, |
| * 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(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: |
| */ |
| if (out_depth <= 8) |
| { |
| if (pm->log8 == 0) /* switched off */ |
| return 256; |
| |
| if (out_depth < 8) |
| return pm->log8 / 255 * ((1<<out_depth)-1); |
| |
| return pm->log8; |
| } |
| |
| if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16) |
| { |
| if (pm->log16 == 0) |
| return 65536; |
| |
| return pm->log16; |
| } |
| |
| /* This is the case where the value was calculated at 8-bit precision then |
| * scaled to 16 bits. |
| */ |
| if (pm->log8 == 0) |
| return 65536; |
| |
| return pm->log8 * 257; |
| } |
| |
| /* This complements the above by providing the appropriate quantization for the |
| * final value. Normally this would just be quantization to an integral value, |
| * but in the 8 bit calculation case it's actually quantization to a multiple of |
| * 257! |
| */ |
| static int output_quantization_factor(const png_modifier *pm, int in_depth, |
| int out_depth) |
| { |
| if (out_depth == 16 && in_depth != 16 && |
| pm->calculations_use_input_precision) |
| return 257; |
| else |
| return 1; |
| } |
| #endif /* PNG_READ_GAMMA_SUPPORTED */ |
| |
| /* One modification structure must be provided for each chunk to be modified (in |
| * fact more than one can be provided if multiple separate changes are desired |
| * for a single chunk.) Modifications include adding a new chunk when a |
| * suitable chunk does not exist. |
| * |
| * The caller of modify_fn will reset the CRC of the chunk and record 'modified' |
| * or 'added' as appropriate if the modify_fn returns 1 (true). If the |
| * modify_fn is NULL the chunk is simply removed. |
| */ |
| typedef struct png_modification |
| { |
| struct png_modification *next; |
| png_uint_32 chunk; |
| |
| /* If the following is NULL all matching chunks will be removed: */ |
| int (*modify_fn)(struct png_modifier *pm, |
| struct png_modification *me, int add); |
| |
| /* If the following is set to PLTE, IDAT or IEND and the chunk has not been |
| * found and modified (and there is a modify_fn) the modify_fn will be called |
| * to add the chunk before the relevant chunk. |
| */ |
| png_uint_32 add; |
| unsigned int modified :1; /* Chunk was modified */ |
| unsigned int added :1; /* Chunk was added */ |
| unsigned int removed :1; /* Chunk was removed */ |
| } png_modification; |
| |
| static void |
| modification_reset(png_modification *pmm) |
| { |
| if (pmm != NULL) |
| { |
| pmm->modified = 0; |
| pmm->added = 0; |
| pmm->removed = 0; |
| modification_reset(pmm->next); |
| } |
| } |
| |
| static void |
| modification_init(png_modification *pmm) |
| { |
| memset(pmm, 0, sizeof *pmm); |
| pmm->next = NULL; |
| pmm->chunk = 0; |
| pmm->modify_fn = NULL; |
| pmm->add = 0; |
| modification_reset(pmm); |
| } |
| |
| #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED |
| static void |
| modifier_current_encoding(const png_modifier *pm, color_encoding *ce) |
| { |
| if (pm->current_encoding != 0) |
| *ce = *pm->current_encoding; |
| |
| else |
| memset(ce, 0, sizeof *ce); |
| |
| ce->gamma = pm->current_gamma; |
| } |
| #endif |
| |
| #ifdef PNG_READ_TRANSFORMS_SUPPORTED |
| static size_t |
| safecat_current_encoding(char *buffer, size_t bufsize, size_t pos, |
| const png_modifier *pm) |
| { |
| pos = safecat_color_encoding(buffer, bufsize, pos, pm->current_encoding, |
| pm->current_gamma); |
| |
| if (pm->encoding_ignored) |
| pos = safecat(buffer, bufsize, pos, "[overridden]"); |
| |
| return pos; |
| } |
| #endif |
| |
| /* Iterate through the usefully testable color encodings. An encoding is one |
| * of: |
| * |
| * 1) Nothing (no color space, no gamma). |
| * 2) Just a gamma value from the gamma array (including 1.0) |
| * 3) A color space from the encodings array with the corresponding gamma. |
| * 4) The same, but with gamma 1.0 (only really useful with 16 bit calculations) |
| * |
| * The iterator selects these in turn, the randomizer selects one at random, |
| * which is used depends on the setting of the 'test_exhaustive' flag. Notice |
| * that this function changes the colour space encoding so it must only be |
| * called on completion of the previous test. This is what 'modifier_reset' |
| * does, below. |
| * |
| * After the function has been called the 'repeat' flag will still be set; the |
| * caller of modifier_reset must reset it at the start of each run of the test! |
| */ |
| static unsigned int |
| modifier_total_encodings(const png_modifier *pm) |
| { |
| return 1 + /* (1) nothing */ |
| pm->ngammas + /* (2) gamma values to test */ |
| pm->nencodings + /* (3) total number of encodings */ |
| /* The following test only works after the first time through the |
| * png_modifier code because 'bit_depth' is set when the IHDR is read. |
| * modifier_reset, below, preserves the setting until after it has called |
| * the iterate function (also below.) |
| * |
| * For this reason do not rely on this function outside a call to |
| * modifier_reset. |
| */ |
| ((pm->bit_depth == 16 || pm->assume_16_bit_calculations) ? |
| pm->nencodings : 0); /* (4) encodings with gamma == 1.0 */ |
| } |
| |
| static void |
| modifier_encoding_iterate(png_modifier *pm) |
| { |
| if (!pm->repeat && /* Else something needs the current encoding again. */ |
| pm->test_uses_encoding) /* Some transform is encoding dependent */ |
| { |
| if (pm->test_exhaustive) |
| { |
| if (++pm->encoding_counter >= modifier_total_encodings(pm)) |
| pm->encoding_counter = 0; /* This will stop the repeat */ |
| } |
| |
| else |
| { |
| /* Not exhaustive - choose an encoding at random; generate a number in |
| * the range 1..(max-1), so the result is always non-zero: |
| */ |
| if (pm->encoding_counter == 0) |
| pm->encoding_counter = random_mod(modifier_total_encodings(pm)-1)+1; |
| else |
| pm->encoding_counter = 0; |
| } |
| |
| if (pm->encoding_counter > 0) |
| pm->repeat = 1; |
| } |
| |
| else if (!pm->repeat) |
| pm->encoding_counter = 0; |
| } |
| |
| static void |
| modifier_reset(png_modifier *pm) |
| { |
| store_read_reset(&pm->this); |
| pm->limit = 4E-3; |
| pm->pending_len = pm->pending_chunk = 0; |
| pm->flush = pm->buffer_count = pm->buffer_position = 0; |
| pm->modifications = NULL; |
| pm->state = modifier_start; |
| modifier_encoding_iterate(pm); |
| /* The following must be set in the next run. In particular |
| * test_uses_encodings must be set in the _ini function of each transform |
| * that looks at the encodings. (Not the 'add' function!) |
| */ |
| pm->test_uses_encoding = 0; |
| pm->current_gamma = 0; |
| pm->current_encoding = 0; |
| pm->encoding_ignored = 0; |
| /* These only become value after IHDR is read: */ |
| pm->bit_depth = pm->colour_type = 0; |
| } |
| |
| /* The following must be called before anything else to get the encoding set up |
| * on the modifier. In particular it must be called before the transform init |
| * functions are called. |
| */ |
| static void |
| modifier_set_encoding(png_modifier *pm) |
| { |
| /* Set the encoding to the one specified by the current encoding counter, |
| * first clear out all the settings - this corresponds to an encoding_counter |
| * of 0. |
| */ |
| pm->current_gamma = 0; |
| pm->current_encoding = 0; |
| pm->encoding_ignored = 0; /* not ignored yet - happens in _ini functions. */ |
| |
| /* Now, if required, set the gamma and encoding fields. */ |
| if (pm->encoding_counter > 0) |
| { |
| /* The gammas[] array is an array of screen gammas, not encoding gammas, |
| * so we need the inverse: |
| */ |
| if (pm->encoding_counter <= pm->ngammas) |
| pm->current_gamma = 1/pm->gammas[pm->encoding_counter-1]; |
| |
| else |
| { |
| unsigned int i = pm->encoding_counter - pm->ngammas; |
| |
| if (i >= pm->nencodings) |
| { |
| i %= pm->nencodings; |
| pm->current_gamma = 1; /* Linear, only in the 16 bit case */ |
| } |
| |
| else |
| pm->current_gamma = pm->encodings[i].gamma; |
| |
| pm->current_encoding = pm->encodings + i; |
| } |
| } |
| } |
| |
| /* Enquiry functions to find out what is set. Notice that there is an implicit |
| * assumption below that the first encoding in the list is the one for sRGB. |
| */ |
| static int |
| 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(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) |
| { |
| /* Recalculate the chunk CRC - a complete chunk must be in |
| * the buffer, at the start. |
| */ |
| uInt datalen = png_get_uint_32(buffer); |
| uLong crc = crc32(0, buffer+4, datalen+4); |
| /* The cast to png_uint_32 is safe because a crc32 is always a 32 bit value. |
| */ |
| png_save_uint_32(buffer+datalen+8, (png_uint_32)crc); |
| } |
| |
| static void |
| modifier_setbuffer(png_modifier *pm) |
| { |
| modifier_crc(pm->buffer); |
| pm->buffer_count = png_get_uint_32(pm->buffer)+12; |
| pm->buffer_position = 0; |
| } |
| |
| /* Separate the callback into the actual implementation (which is passed the |
| * png_modifier explicitly) and the callback, which gets the modifier from the |
| * png_struct. |
| */ |
| static void |
| modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st) |
| { |
| while (st > 0) |
| { |
| size_t cb; |
| png_uint_32 len, chunk; |
| png_modification *mod; |
| |
| if (pm->buffer_position >= pm->buffer_count) switch (pm->state) |
| { |
| 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. */ |
| pm->buffer_count = 8; |
| pm->buffer_position = 0; |
| |
| if (memcmp(pm->buffer, sign, 8) != 0) |
| png_error(pm->this.pread, "invalid PNG file signature"); |
| pm->state = modifier_signature; |
| break; |
| |
| case modifier_signature: |
| store_read_imp(&pm->this, pm->buffer, 13+12); /* size of IHDR */ |
| pm->buffer_count = 13+12; |
| pm->buffer_position = 0; |
| |
| if (png_get_uint_32(pm->buffer) != 13 || |
| png_get_uint_32(pm->buffer+4) != CHUNK_IHDR) |
| png_error(pm->this.pread, "invalid IHDR"); |
| |
| /* Check the list of modifiers for modifications to the IHDR. */ |
| mod = pm->modifications; |
| while (mod != NULL) |
| { |
| if (mod->chunk == CHUNK_IHDR && mod->modify_fn && |
| (*mod->modify_fn)(pm, mod, 0)) |
| { |
| mod->modified = 1; |
| modifier_setbuffer(pm); |
| } |
| |
| /* Ignore removal or add if IHDR! */ |
| mod = mod->next; |
| } |
| |
| /* Cache information from the IHDR (the modified one.) */ |
| pm->bit_depth = pm->buffer[8+8]; |
| pm->colour_type = pm->buffer[8+8+1]; |
| |
| pm->state = modifier_IHDR; |
| pm->flush = 0; |
| break; |
| |
| case modifier_IHDR: |
| default: |
| /* Read a new chunk and process it until we see PLTE, IDAT or |
| * IEND. 'flush' indicates that there is still some data to |
| * output from the preceding chunk. |
| */ |
| if ((cb = pm->flush) > 0) |
| { |
| if (cb > st) cb = st; |
| pm->flush -= cb; |
| store_read_imp(&pm->this, pb, cb); |
| pb += cb; |
| st -= cb; |
| if (st == 0) return; |
| } |
| |
| /* No more bytes to flush, read a header, or handle a pending |
| * chunk. |
| */ |
| if (pm->pending_chunk != 0) |
| { |
| png_save_uint_32(pm->buffer, pm->pending_len); |
| png_save_uint_32(pm->buffer+4, pm->pending_chunk); |
| pm->pending_len = 0; |
| pm->pending_chunk = 0; |
| } |
| else |
| store_read_imp(&pm->this, pm->buffer, 8); |
| |
| pm->buffer_count = 8; |
| pm->buffer_position = 0; |
| |
| /* Check for something to modify or a terminator chunk. */ |
| len = png_get_uint_32(pm->buffer); |
| chunk = png_get_uint_32(pm->buffer+4); |
| |
| /* Terminators first, they may have to be delayed for added |
| * chunks |
| */ |
| if (chunk == CHUNK_PLTE || chunk == CHUNK_IDAT || |
| chunk == CHUNK_IEND) |
| { |
| mod = pm->modifications; |
| |
| while (mod != NULL) |
| { |
| if ((mod->add == chunk || |
| (mod->add == CHUNK_PLTE && chunk == CHUNK_IDAT)) && |
| mod->modify_fn != NULL && !mod->modified && !mod->added) |
| { |
| /* Regardless of what the modify function does do not run |
| * this again. |
| */ |
| mod->added = 1; |
| |
| if ((*mod->modify_fn)(pm, mod, 1 /*add*/)) |
| { |
| /* Reset the CRC on a new chunk */ |
| if (pm->buffer_count > 0) |
| modifier_setbuffer(pm); |
| |
| else |
| { |
| pm->buffer_position = 0; |
| mod->removed = 1; |
| } |
| |
| /* The buffer has been filled with something (we assume) |
| * so output this. Pend the current chunk. |
| */ |
| pm->pending_len = len; |
| pm->pending_chunk = chunk; |
| break; /* out of while */ |
| } |
| } |
| |
| mod = mod->next; |
| } |
| |
| /* Don't do any further processing if the buffer was modified - |
| * otherwise the code will end up modifying a chunk that was |
| * just added. |
| */ |
| if (mod != NULL) |
| break; /* out of switch */ |
| } |
| |
| /* If we get to here then this chunk may need to be modified. To |
| * do this it must be less than 1024 bytes in total size, otherwise |
| * it just gets flushed. |
| */ |
| if (len+12 <= sizeof pm->buffer) |
| { |
| store_read_imp(&pm->this, pm->buffer+pm->buffer_count, |
| len+12-pm->buffer_count); |
| pm->buffer_count = len+12; |
| |
| /* Check for a modification, else leave it be. */ |
| mod = pm->modifications; |
| while (mod != NULL) |
| { |
| if (mod->chunk == chunk) |
| { |
| if (mod->modify_fn == NULL) |
| { |
| /* Remove this chunk */ |
| pm->buffer_count = pm->buffer_position = 0; |
| mod->removed = 1; |
| break; /* Terminate the while loop */ |
| } |
| |
| else if ((*mod->modify_fn)(pm, mod, 0)) |
| { |
| mod->modified = 1; |
| /* The chunk may have been removed: */ |
| if (pm->buffer_count == 0) |
| { |
| pm->buffer_position = 0; |
| break; |
| } |
| modifier_setbuffer(pm); |
| } |
| } |
| |
| mod = mod->next; |
| } |
| } |
| |
| else |
| pm->flush = len+12 - pm->buffer_count; /* data + crc */ |
| |
| /* Take the data from the buffer (if there is any). */ |
| break; |
| } |
| |
| /* Here to read from the modifier buffer (not directly from |
| * the store, as in the flush case above.) |
| */ |
| cb = pm->buffer_count - pm->buffer_position; |
| |
| if (cb > st) |
| cb = st; |
| |
| memcpy(pb, pm->buffer + pm->buffer_position, cb); |
| st -= cb; |
| pb += cb; |
| pm->buffer_position += cb; |
| } |
| } |
| |
| /* The callback: */ |
| static void PNGCBAPI |
| modifier_read(png_structp ppIn, png_bytep pb, png_size_t st) |
| { |
| png_const_structp pp = ppIn; |
| png_modifier *pm = voidcast(png_modifier*, png_get_io_ptr(pp)); |
| |
| if (pm == NULL || pm->this.pread != pp) |
| png_error(pp, "bad modifier_read call"); |
| |
| modifier_read_imp(pm, pb, st); |
| } |
| |
| /* Like store_progressive_read but the data is getting changed as we go so we |
| * need a local buffer. |
| */ |
| static void |
| modifier_progressive_read(png_modifier *pm, png_structp pp, png_infop pi) |
| { |
| if (pm->this.pread != pp || pm->this.current == NULL || |
| pm->this.next == NULL) |
| png_error(pp, "store state damaged (progressive)"); |
| |
| /* 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. |
| */ |
| for (;;) |
| { |
| static png_uint_32 noise = 1; |
| png_size_t cb, cbAvail; |
| png_byte buffer[512]; |
| |
| /* Generate 15 more bits of stuff: */ |
| noise = (noise << 9) | ((noise ^ (noise >> (9-5))) & 0x1ff); |
| cb = noise & 0x1ff; |
| |
| /* Check that this number of bytes are available (in the current buffer.) |
| * (This doesn't quite work - the modifier might delete a chunk; unlikely |
| * but possible, it doesn't happen at present because the modifier only |
| * adds chunks to standard images.) |
| */ |
| cbAvail = store_read_buffer_avail(&pm->this); |
| if (pm->buffer_count > pm->buffer_position) |
| cbAvail += pm->buffer_count - pm->buffer_position; |
| |
| if (cb > cbAvail) |
| { |
| /* Check for EOF: */ |
| if (cbAvail == 0) |
| break; |
| |
| cb = cbAvail; |
| } |
| |
| modifier_read_imp(pm, buffer, cb); |
| png_process_data(pp, pi, buffer, cb); |
| } |
| |
| /* Check the invariants at the end (if this fails it's a problem in this |
| * file!) |
| */ |
| if (pm->buffer_count > pm->buffer_position || |
| pm->this.next != &pm->this.current->data || |
| pm->this.readpos < pm->this.current->datacount) |
| png_error(pp, "progressive read implementation error"); |
| } |
| |
| /* Set up a modifier. */ |
| static png_structp |
| set_modifier_for_read(png_modifier *pm, png_infopp ppi, png_uint_32 id, |
| 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 |
| * cleanup is required. |
| */ |
| pm->state = modifier_start; |
| pm->bit_depth = 0; |
| pm->colour_type = 255; |
| |
| pm->pending_len = 0; |
| pm->pending_chunk = 0; |
| pm->flush = 0; |
| pm->buffer_count = 0; |
| pm->buffer_position = 0; |
| |
| return set_store_for_read(&pm->this, ppi, id, name); |
| } |
| |
| |
| /******************************** MODIFICATIONS *******************************/ |
| /* Standard modifications to add chunks. These do not require the _SUPPORTED |
| * macros because the chunks can be there regardless of whether this specific |
| * libpng supports them. |
| */ |
| typedef struct gama_modification |
| { |
| png_modification this; |
| png_fixed_point gamma; |
| } gama_modification; |
| |
| static int |
| gama_modify(png_modifier *pm, png_modification *me, int add) |
| { |
| UNUSED(add) |
| /* This simply dumps the given gamma value into the buffer. */ |
| png_save_uint_32(pm->buffer, 4); |
| png_save_uint_32(pm->buffer+4, CHUNK_gAMA); |
| png_save_uint_32(pm->buffer+8, ((gama_modification*)me)->gamma); |
| return 1; |
| } |
| |
| static void |
| gama_modification_init(gama_modification *me, png_modifier *pm, double gammad) |
| { |
| double g; |
| |
| modification_init(&me->this); |
| me->this.chunk = CHUNK_gAMA; |
| me->this.modify_fn = gama_modify; |
| me->this.add = CHUNK_PLTE; |
| g = fix(gammad); |
| me->gamma = (png_fixed_point)g; |
| me->this.next = pm->modifications; |
| pm->modifications = &me->this; |
| } |
| |
| typedef struct chrm_modification |
| { |
| png_modification this; |
| const color_encoding *encoding; |
| png_fixed_point wx, wy, rx, ry, gx, gy, bx, by; |
| } chrm_modification; |
| |
| static int |
| chrm_modify(png_modifier *pm, png_modification *me, int add) |
| { |
| UNUSED(add) |
| /* As with gAMA this just adds the required cHRM chunk to the buffer. */ |
| png_save_uint_32(pm->buffer , 32); |
| png_save_uint_32(pm->buffer+ 4, CHUNK_cHRM); |
| png_save_uint_32(pm->buffer+ 8, ((chrm_modification*)me)->wx); |
| png_save_uint_32(pm->buffer+12, ((chrm_modification*)me)->wy); |
| png_save_uint_32(pm->buffer+16, ((chrm_modification*)me)->rx); |
| png_save_uint_32(pm->buffer+20, ((chrm_modification*)me)->ry); |
| png_save_uint_32(pm->buffer+24, ((chrm_modification*)me)->gx); |
| png_save_uint_32(pm->buffer+28, ((chrm_modification*)me)->gy); |
| png_save_uint_32(pm->buffer+32, ((chrm_modification*)me)->bx); |
| png_save_uint_32(pm->buffer+36, ((chrm_modification*)me)->by); |
| return 1; |
| } |
| |
| static void |
| chrm_modification_init(chrm_modification *me, png_modifier *pm, |
| const color_encoding *encoding) |
| { |
| CIE_color white = white_point(encoding); |
| |
| /* Original end points: */ |
| me->encoding = encoding; |
| |
| /* Chromaticities (in fixed point): */ |
| me->wx = fix(chromaticity_x(white)); |
| me->wy = fix(chromaticity_y(white)); |
| |
| me->rx = fix(chromaticity_x(encoding->red)); |
| me->ry = fix(chromaticity_y(encoding->red)); |
| me->gx = fix(chromaticity_x(encoding->green)); |
| me->gy = fix(chromaticity_y(encoding->green)); |
| me->bx = fix(chromaticity_x(encoding->blue)); |
| me->by = fix(chromaticity_y(encoding->blue)); |
| |
| modification_init(&me->this); |
| me->this.chunk = CHUNK_cHRM; |
| me->this.modify_fn = chrm_modify; |
| me->this.add = CHUNK_PLTE; |
| me->this.next = pm->modifications; |
| pm->modifications = &me->this; |
| } |
| |
| typedef struct srgb_modification |
| { |
| png_modification this; |
| png_byte intent; |
| } srgb_modification; |
| |
| static int |
| srgb_modify(png_modifier *pm, png_modification *me, int add) |
| { |
| UNUSED(add) |
| /* As above, ignore add and just make a new chunk */ |
| png_save_uint_32(pm->buffer, 1); |
| png_save_uint_32(pm->buffer+4, CHUNK_sRGB); |
| pm->buffer[8] = ((srgb_modification*)me)->intent; |
| return 1; |
| } |
| |
| static void |
| srgb_modification_init(srgb_modification *me, png_modifier *pm, png_byte intent) |
| { |
| modification_init(&me->this); |
| me->this.chunk = CHUNK_sBIT; |
| |
| if (intent <= 3) /* if valid, else *delete* sRGB chunks */ |
| { |
| me->this.modify_fn = srgb_modify; |
| me->this.add = CHUNK_PLTE; |
| me->intent = intent; |
| } |
| |
| else |
| { |
| me->this.modify_fn = 0; |
| me->this.add = 0; |
| me->intent = 0; |
| } |
| |
| me->this.next = pm->modifications; |
| pm->modifications = &me->this; |
| } |
| |
| #ifdef PNG_READ_GAMMA_SUPPORTED |
| typedef struct sbit_modification |
| { |
| png_modification this; |
| png_byte sbit; |
| } sbit_modification; |
| |
| static int |
| sbit_modify(png_modifier *pm, png_modification *me, int add) |
| { |
| png_byte sbit = ((sbit_modification*)me)->sbit; |
| if (pm->bit_depth > sbit) |
| { |
| int cb = 0; |
| switch (pm->colour_type) |
| { |
| case 0: |
| cb = 1; |
| break; |
| |
| case 2: |
| case 3: |
| cb = 3; |
| break; |
| |
| case 4: |
| cb = 2; |
| break; |
| |
| case 6: |
| cb = 4; |
| break; |
| |
| default: |
| png_error(pm->this.pread, |
| "unexpected colour type in sBIT modification"); |
| } |
| |
| png_save_uint_32(pm->buffer, cb); |
| png_save_uint_32(pm->buffer+4, CHUNK_sBIT); |
| |
| while (cb > 0) |
| (pm->buffer+8)[--cb] = sbit; |
| |
| return 1; |
| } |
| else if (!add) |
| { |
| /* Remove the sBIT chunk */ |
| pm->buffer_count = pm->buffer_position = 0; |
| return 1; |
| } |
| else |
| return 0; /* do nothing */ |
| } |
| |
| static void |
| sbit_modification_init(sbit_modification *me, png_modifier *pm, png_byte sbit) |
| { |
| modification_init(&me->this); |
| me->this.chunk = CHUNK_sBIT; |
| me->this.modify_fn = sbit_modify; |
| me->this.add = CHUNK_PLTE; |
| me->sbit = sbit; |
| me->this.next = pm->modifications; |
| pm->modifications = &me->this; |
| } |
| #endif /* PNG_READ_GAMMA_SUPPORTED */ |
| #endif /* PNG_READ_TRANSFORMS_SUPPORTED */ |
| |
| /***************************** STANDARD PNG FILES *****************************/ |
| /* Standard files - write and save standard files. */ |
| /* There are two basic forms of standard images. Those which attempt to have |
| * all the possible pixel values (not possible for 16bpp images, but a range of |
| * values are produced) and those which have a range of image sizes. The former |
| * are used for testing transforms, in particular gamma correction and bit |
| * reduction and increase. The latter are reserved for testing the behavior of |
| * libpng with respect to 'odd' image sizes - particularly small images where |
| * rows become 1 byte and interlace passes disappear. |
| * |
| * The first, most useful, set are the 'transform' images, the second set of |
| * small images are the 'size' images. |
| * |
| * The transform files are constructed with rows which fit into a 1024 byte row |
| * buffer. This makes allocation easier below. Further regardless of the file |
| * format every row has 128 pixels (giving 1024 bytes for 64bpp formats). |
| * |
| * Files are stored with no gAMA or sBIT chunks, with a PLTE only when needed |
| * and with an ID derived from the colour type, bit depth and interlace type |
| * as above (FILEID). The width (128) and height (variable) are not stored in |
| * the FILEID - instead the fields are set to 0, indicating a transform file. |
| * |
| * The size files ar constructed with rows a maximum of 128 bytes wide, allowing |
| * a maximum width of 16 pixels (for the 64bpp case.) They also have a maximum |
| * height of 16 rows. The width and height are stored in the FILEID and, being |
| * non-zero, indicate a size file. |
| * |
| * Because the PNG filter code is typically the largest CPU consumer within |
| * libpng itself there is a tendency to attempt to optimize it. This results in |
| * special case code which needs to be validated. To cause this to happen the |
| * 'size' images are made to use each possible filter, in so far as this is |
| * possible for smaller images. |
| * |
| * For palette image (colour type 3) multiple transform images are stored with |
| * the same bit depth to allow testing of more colour combinations - |
| * particularly important for testing the gamma code because libpng uses a |
| * different code path for palette images. For size images a single palette is |
| * used. |
| */ |
| |
| /* Make a 'standard' palette. Because there are only 256 entries in a palette |
| * (maximum) this actually makes a random palette in the hope that enough tests |
| * will catch enough errors. (Note that the same palette isn't produced every |
| * time for the same test - it depends on what previous tests have been run - |
| * but a given set of arguments to pngvalid will always produce the same palette |
| * at the same test! This is why pseudo-random number generators are useful for |
|