blob: 069f8e2741473684165e9c8515ca3e0e26fd7f5b [file] [log] [blame]
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001
2/* pngvalid.c - validate libpng by constructing then reading png files.
3 *
Glenn Randers-Pehrson47539062011-05-05 07:32:30 -05004 * Last changed in libpng 1.5.3 [(PENDING RELEASE)]
Glenn Randers-Pehrson64b863c2011-01-04 09:57:06 -06005 * Copyright (c) 2011 Glenn Randers-Pehrson
John Bowler660c6e42010-12-19 06:22:23 -06006 * Written by John Cunningham Bowler
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007 *
8 * This code is released under the libpng license.
9 * For conditions of distribution and use, see the disclaimer
10 * and license in png.h
11 *
12 * NOTES:
13 * This is a C program that is intended to be linked against libpng. It
14 * generates bitmaps internally, stores them as PNG files (using the
15 * sequential write code) then reads them back (using the sequential
16 * read code) and validates that the result has the correct data.
17 *
18 * The program can be modified and extended to test the correctness of
19 * transformations performed by libpng.
20 */
21
Glenn Randers-Pehrsonb3b71682011-05-03 22:30:19 -050022#define _POSIX_SOURCE 1
23
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050024#include "png.h"
John Bowlerf21a0d02011-01-23 23:55:19 -060025#if PNG_LIBPNG_VER < 10500
26/* This delibarately lacks the PNG_CONST. */
27typedef png_byte *png_const_bytep;
28
29/* This is copied from 1.5.1 png.h: */
30#define PNG_INTERLACE_ADAM7_PASSES 7
31#define PNG_PASS_START_ROW(pass) (((1U&~(pass))<<(3-((pass)>>1)))&7)
32#define PNG_PASS_START_COL(pass) (((1U& (pass))<<(3-(((pass)+1)>>1)))&7)
33#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3)
34#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3)
35#define PNG_PASS_ROWS(height, pass) (((height)+(((1<<PNG_PASS_ROW_SHIFT(pass))\
36 -1)-PNG_PASS_START_ROW(pass)))>>PNG_PASS_ROW_SHIFT(pass))
37#define PNG_PASS_COLS(width, pass) (((width)+(((1<<PNG_PASS_COL_SHIFT(pass))\
38 -1)-PNG_PASS_START_COL(pass)))>>PNG_PASS_COL_SHIFT(pass))
39#define PNG_ROW_FROM_PASS_ROW(yIn, pass) \
40 (((yIn)<<PNG_PASS_ROW_SHIFT(pass))+PNG_PASS_START_ROW(pass))
41#define PNG_COL_FROM_PASS_COL(xIn, pass) \
42 (((xIn)<<PNG_PASS_COL_SHIFT(pass))+PNG_PASS_START_COL(pass))
43#define PNG_PASS_MASK(pass,off) ( \
44 ((0x110145AFU>>(((7-(off))-(pass))<<2)) & 0xFU) | \
45 ((0x01145AF0U>>(((7-(off))-(pass))<<2)) & 0xF0U))
46#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \
47 ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1)
48#define PNG_COL_IN_INTERLACE_PASS(x, pass) \
49 ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1)
50
John Bowlerd273ad22011-05-07 21:00:28 -050051/* These are needed too for the default build: */
John Bowlerf21a0d02011-01-23 23:55:19 -060052#define PNG_WRITE_16BIT_SUPPORTED
53#define PNG_READ_16BIT_SUPPORTED
John Bowlerd273ad22011-05-07 21:00:28 -050054
55/* This comes from pnglibconf.h afer 1.5: */
56#define PNG_GAMMA_THRESHOLD_FIXED\
57 ((png_fixed_point)(PNG_GAMMA_THRESHOLD * 100000))
John Bowlerf21a0d02011-01-23 23:55:19 -060058#endif
59
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050060#include "zlib.h" /* For crc32 */
61
John Bowlerf21a0d02011-01-23 23:55:19 -060062#include <float.h> /* For floating point constants */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050063#include <stdlib.h> /* For malloc */
64#include <string.h> /* For memcpy, memset */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050065#include <math.h> /* For floor */
66
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -050067/* Unused formal parameter errors are removed using the following macro which is
68 * expected to have no bad effects on performance.
69 */
70#ifndef UNUSED
John Bowlerafea7d12011-01-28 06:38:14 -060071# if defined(__GNUC__) || defined(_MSC_VER)
72# define UNUSED(param) (void)param;
73# else
74# define UNUSED(param)
75# endif
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -050076#endif
77
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -050078/***************************** EXCEPTION HANDLING *****************************/
79#include "contrib/visupng/cexcept.h"
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -050080struct png_store;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -050081define_exception_type(struct png_store*);
82
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -050083/* The following are macros to reduce typing everywhere where the well known
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -050084 * name 'the_exception_context' must be defined.
85 */
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -050086#define anon_context(ps) struct exception_context *the_exception_context = \
87 &(ps)->exception_context
88#define context(ps,fault) anon_context(ps); png_store *fault
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -050089
John Bowler660c6e42010-12-19 06:22:23 -060090/******************************* UTILITIES ************************************/
91/* Error handling is particularly problematic in production code - error
92 * handlers often themselves have bugs which lead to programs that detect
93 * minor errors crashing. The following functions deal with one very
94 * common class of errors in error handlers - attempting to format error or
95 * warning messages into buffers that are too small.
96 */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -050097static size_t safecat(char *buffer, size_t bufsize, size_t pos,
98 PNG_CONST char *cat)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050099{
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500100 while (pos < bufsize && cat != NULL && *cat != 0)
101 buffer[pos++] = *cat++;
102
103 if (pos >= bufsize)
104 pos = bufsize-1;
105
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500106 buffer[pos] = 0;
107 return pos;
108}
109
110static size_t safecatn(char *buffer, size_t bufsize, size_t pos, int n)
111{
112 char number[64];
113 sprintf(number, "%d", n);
114 return safecat(buffer, bufsize, pos, number);
115}
116
John Bowler4a12f4a2011-04-17 18:34:22 -0500117#ifdef PNG_READ_TRANSFORMS_SUPPORTED
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500118static size_t safecatd(char *buffer, size_t bufsize, size_t pos, double d,
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500119 int precision)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500120{
121 char number[64];
122 sprintf(number, "%.*f", precision, d);
123 return safecat(buffer, bufsize, pos, number);
124}
John Bowler4a12f4a2011-04-17 18:34:22 -0500125#endif
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500126
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -0500127static PNG_CONST char invalid[] = "invalid";
128static PNG_CONST char sep[] = ": ";
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500129
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -0500130static PNG_CONST char *colour_types[8] =
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500131{
132 "greyscale", invalid, "truecolour", "indexed-colour",
133 "greyscale with alpha", invalid, "truecolour with alpha", invalid
134};
135
John Bowler9994f252011-05-15 18:52:39 -0500136/* Generate random bytes. This uses a boring repeatable algorithm and it
137 * is implemented here so that it gives the same set of numbers on every
138 * architecture. It's a linear congruential generator (Knuth or Sedgewick
139 * "Algorithms") but it comes from the 'feedback taps' table in Horowitz and
140 * Hill, "The Art of Electronics".
141 */
142static void
143make_random_bytes(png_uint_32* seed, void* pv, size_t size)
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500144{
John Bowler9994f252011-05-15 18:52:39 -0500145 png_uint_32 u0 = seed[0], u1 = seed[1];
146 png_bytep bytes = /*no cast required*/pv;
147
148 /* There are thirty three bits, the next bit in the sequence is bit-33 XOR
149 * bit-20. The top 1 bit is in u1, the bottom 32 are in u0.
150 */
151 size_t i;
152 for (i=0; i<size; ++i)
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500153 {
John Bowler9994f252011-05-15 18:52:39 -0500154 /* First generate 8 new bits then shift them in at the end. */
155 png_uint_32 u = ((u0 >> (20-8)) ^ ((u1 << 7) | (u0 >> (32-7)))) & 0xff;
156 u1 <<= 8;
157 u1 |= u0 >> 24;
158 u0 <<= 8;
159 u0 |= u;
160 *bytes++ = (png_byte)u;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500161 }
John Bowler9994f252011-05-15 18:52:39 -0500162
163 seed[0] = u0;
164 seed[1] = u1;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500165}
166
John Bowler9994f252011-05-15 18:52:39 -0500167static void
168make_four_random_bytes(png_uint_32* seed, png_bytep bytes)
169{
170 make_random_bytes(seed, bytes, 4);
171}
172
173static void
174randomize(void *pv, size_t size)
175{
176 static png_uint_32 random_seed[2] = {0x56789abc, 0xd};
177 make_random_bytes(random_seed, pv, size);
178}
179
180#define RANDOMIZE(this) randomize(&(this), sizeof (this))
181
John Bowler660c6e42010-12-19 06:22:23 -0600182/* A numeric ID based on PNG file characteristics. The 'do_interlace' field
183 * simply records whether pngvalid did the interlace itself or whether it
John Bowler9994f252011-05-15 18:52:39 -0500184 * was done by libpng. Width and height must be less than 256. 'palette' is an
185 * index of the palette to use for formats with a palette (0 otherwise.)
John Bowler660c6e42010-12-19 06:22:23 -0600186 */
John Bowler9994f252011-05-15 18:52:39 -0500187#define FILEID(col, depth, palette, interlace, width, height, do_interlace) \
188 ((png_uint_32)((col) + ((depth)<<3) + ((palette)<<8) + ((interlace)<<13) + \
John Bowler660c6e42010-12-19 06:22:23 -0600189 (((do_interlace)!=0)<<15) + ((width)<<16) + ((height)<<24)))
190
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500191#define COL_FROM_ID(id) ((png_byte)((id)& 0x7U))
192#define DEPTH_FROM_ID(id) ((png_byte)(((id) >> 3) & 0x1fU))
John Bowler9994f252011-05-15 18:52:39 -0500193#define PALETTE_FROM_ID(id) ((int)(((id) >> 8) & 0x1f))
194#define INTERLACE_FROM_ID(id) ((int)(((id) >> 13) & 0x3))
John Bowler660c6e42010-12-19 06:22:23 -0600195#define DO_INTERLACE_FROM_ID(id) ((int)(((id)>>15) & 1))
196#define WIDTH_FROM_ID(id) (((id)>>16) & 0xff)
197#define HEIGHT_FROM_ID(id) (((id)>>24) & 0xff)
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500198
199/* Utility to construct a standard name for a standard image. */
200static size_t
201standard_name(char *buffer, size_t bufsize, size_t pos, png_byte colour_type,
John Bowler9994f252011-05-15 18:52:39 -0500202 int bit_depth, int npalette, int interlace_type,
203 png_uint_32 w, png_uint_32 h, int do_interlace)
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500204{
205 pos = safecat(buffer, bufsize, pos, colour_types[colour_type]);
John Bowler9994f252011-05-15 18:52:39 -0500206 if (npalette > 0)
207 {
208 pos = safecat(buffer, bufsize, pos, "[");
209 pos = safecatn(buffer, bufsize, pos, npalette);
210 pos = safecat(buffer, bufsize, pos, "]");
211 }
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500212 pos = safecat(buffer, bufsize, pos, " ");
John Bowler9994f252011-05-15 18:52:39 -0500213 pos = safecatn(buffer, bufsize, pos, bit_depth);
John Bowler660c6e42010-12-19 06:22:23 -0600214 pos = safecat(buffer, bufsize, pos, " bit ");
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500215
216 if (interlace_type != PNG_INTERLACE_NONE)
John Bowler9994f252011-05-15 18:52:39 -0500217 {
John Bowler660c6e42010-12-19 06:22:23 -0600218 pos = safecat(buffer, bufsize, pos, "interlaced");
John Bowler9994f252011-05-15 18:52:39 -0500219 if (do_interlace)
220 pos = safecat(buffer, bufsize, pos, "(pngvalid)");
221 else
222 pos = safecat(buffer, bufsize, pos, "(libpng)");
223 }
224
John Bowler660c6e42010-12-19 06:22:23 -0600225 if (w > 0 || h > 0)
226 {
227 pos = safecat(buffer, bufsize, pos, " ");
228 pos = safecatn(buffer, bufsize, pos, w);
229 pos = safecat(buffer, bufsize, pos, "x");
230 pos = safecatn(buffer, bufsize, pos, h);
231 }
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500232
233 return pos;
234}
235
236static size_t
237standard_name_from_id(char *buffer, size_t bufsize, size_t pos, png_uint_32 id)
238{
239 return standard_name(buffer, bufsize, pos, COL_FROM_ID(id),
John Bowler9994f252011-05-15 18:52:39 -0500240 DEPTH_FROM_ID(id), PALETTE_FROM_ID(id), INTERLACE_FROM_ID(id),
John Bowler660c6e42010-12-19 06:22:23 -0600241 WIDTH_FROM_ID(id), HEIGHT_FROM_ID(id), DO_INTERLACE_FROM_ID(id));
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500242}
243
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -0500244/* Convenience API and defines to list valid formats. Note that 16 bit read and
245 * write support is required to do 16 bit read tests (we must be able to make a
246 * 16 bit image to test!)
247 */
248#ifdef PNG_WRITE_16BIT_SUPPORTED
249# define WRITE_BDHI 4
250# ifdef PNG_READ_16BIT_SUPPORTED
251# define READ_BDHI 4
252# define DO_16BIT
253# endif
254#else
255# define WRITE_BDHI 3
256#endif
257#ifndef DO_16BIT
258# define READ_BDHI 3
259#endif
260
John Bowler9994f252011-05-15 18:52:39 -0500261/* The following defines the number of different palettes to generate for
262 * each log bit depth of a colour type 3 standard image.
263 */
264#define PALETTE_COUNT(bit_depth) ((bit_depth) > 4 ? 1 : 16)
265
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500266static int
John Bowler9994f252011-05-15 18:52:39 -0500267next_format(png_bytep colour_type, png_bytep bit_depth, int* palette_number)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500268{
269 if (*bit_depth == 0)
270 {
John Bowler9994f252011-05-15 18:52:39 -0500271 *colour_type = 0, *bit_depth = 1, *palette_number = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500272 return 1;
273 }
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -0500274
John Bowler9994f252011-05-15 18:52:39 -0500275 if (*colour_type == 3)
276 {
277 /* Add multiple palettes for colour type 3. */
278 if (++*palette_number < PALETTE_COUNT(*bit_depth))
279 return 1;
280
281 *palette_number = 0;
282 }
283
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -0500284 *bit_depth = (png_byte)(*bit_depth << 1);
285
286 /* Palette images are restricted to 8 bit depth */
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -0500287 if (*bit_depth <= 8
288# ifdef DO_16BIT
289 || (*colour_type != 3 && *bit_depth <= 16)
290# endif
291 )
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -0500292 return 1;
293
294 /* Move to the next color type, or return 0 at the end. */
295 switch (*colour_type)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500296 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500297 case 0:
298 *colour_type = 2;
299 *bit_depth = 8;
300 return 1;
301
302 case 2:
303 *colour_type = 3;
304 *bit_depth = 1;
305 return 1;
306
307 case 3:
308 *colour_type = 4;
309 *bit_depth = 8;
310 return 1;
311
312 case 4:
313 *colour_type = 6;
314 *bit_depth = 8;
315 return 1;
316
317 default:
318 return 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500319 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500320}
321
John Bowler9994f252011-05-15 18:52:39 -0500322#ifdef PNG_READ_TRANSFORMS_SUPPORTED
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -0500323static unsigned int
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500324sample(png_const_bytep row, png_byte colour_type, png_byte bit_depth,
John Bowler168a4332011-01-16 19:32:22 -0600325 png_uint_32 x, unsigned int sample_index)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500326{
John Bowler168a4332011-01-16 19:32:22 -0600327 png_uint_32 bit_index, result;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -0600328
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500329 /* Find a sample index for the desired sample: */
330 x *= bit_depth;
John Bowler168a4332011-01-16 19:32:22 -0600331 bit_index = x;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500332
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500333 if ((colour_type & 1) == 0) /* !palette */
334 {
335 if (colour_type & 2)
John Bowler168a4332011-01-16 19:32:22 -0600336 bit_index *= 3;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500337
338 if (colour_type & 4)
John Bowler168a4332011-01-16 19:32:22 -0600339 bit_index += x; /* Alpha channel */
John Bowlere2062f92011-01-15 22:36:33 -0600340
John Bowlerd273ad22011-05-07 21:00:28 -0500341 /* Multiple channels; select one: */
John Bowlere2062f92011-01-15 22:36:33 -0600342 if (colour_type & (2+4))
John Bowlerd273ad22011-05-07 21:00:28 -0500343 bit_index += sample_index * bit_depth;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500344 }
345
346 /* Return the sample from the row as an integer. */
John Bowler168a4332011-01-16 19:32:22 -0600347 row += bit_index >> 3;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500348 result = *row;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500349
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500350 if (bit_depth == 8)
351 return result;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500352
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500353 else if (bit_depth > 8)
354 return (result << 8) + *++row;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500355
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500356 /* Less than 8 bits per sample. */
John Bowler168a4332011-01-16 19:32:22 -0600357 bit_index &= 7;
358 return (result >> (8-bit_index-bit_depth)) & ((1U<<bit_depth)-1);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500359}
John Bowler4a12f4a2011-04-17 18:34:22 -0500360#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500361
John Bowler660c6e42010-12-19 06:22:23 -0600362/* Copy a single pixel, of a given size, from one buffer to another -
363 * while this is basically bit addressed there is an implicit assumption
364 * that pixels 8 or more bits in size are byte aligned and that pixels
365 * do not otherwise cross byte boundaries. (This is, so far as I know,
366 * universally true in bitmap computer graphics. [JCB 20101212])
367 *
368 * NOTE: The to and from buffers may be the same.
369 */
370static void
371pixel_copy(png_bytep toBuffer, png_uint_32 toIndex,
372 png_const_bytep fromBuffer, png_uint_32 fromIndex, unsigned int pixelSize)
373{
374 /* Assume we can multiply by 'size' without overflow because we are
375 * just working in a single buffer.
376 */
377 toIndex *= pixelSize;
378 fromIndex *= pixelSize;
379 if (pixelSize < 8) /* Sub-byte */
380 {
381 /* Mask to select the location of the copied pixel: */
382 unsigned int destMask = ((1U<<pixelSize)-1) << (8-pixelSize-(toIndex&7));
383 /* The following read the entire pixels and clears the extra: */
384 unsigned int destByte = toBuffer[toIndex >> 3] & ~destMask;
385 unsigned int sourceByte = fromBuffer[fromIndex >> 3];
386
387 /* Don't rely on << or >> supporting '0' here, just in case: */
388 fromIndex &= 7;
389 if (fromIndex > 0) sourceByte <<= fromIndex;
390 if ((toIndex & 7) > 0) sourceByte >>= toIndex & 7;
391
392 toBuffer[toIndex >> 3] = (png_byte)(destByte | (sourceByte & destMask));
393 }
394 else /* One or more bytes */
395 memmove(toBuffer+(toIndex>>3), fromBuffer+(fromIndex>>3), pixelSize>>3);
396}
397
398/* Compare pixels - they are assumed to start at the first byte in the
399 * given buffers.
400 */
401static int
402pixel_cmp(png_const_bytep pa, png_const_bytep pb, png_uint_32 bit_width)
403{
404 if (memcmp(pa, pb, bit_width>>3) == 0)
405 {
406 png_uint_32 p;
407
408 if ((bit_width & 7) == 0) return 0;
409
410 /* Ok, any differences? */
411 p = pa[bit_width >> 3];
412 p ^= pb[bit_width >> 3];
413
414 if (p == 0) return 0;
415
416 /* There are, but they may not be significant, remove the bits
417 * after the end (the low order bits in PNG.)
418 */
419 bit_width &= 7;
420 p >>= 8-bit_width;
421
422 if (p == 0) return 0;
423 }
424
425 return 1; /* Different */
426}
427
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500428/*************************** BASIC PNG FILE WRITING ***************************/
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500429/* A png_store takes data from the sequential writer or provides data
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500430 * to the sequential reader. It can also store the result of a PNG
431 * write for later retrieval.
432 */
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500433#define STORE_BUFFER_SIZE 500 /* arbitrary */
434typedef struct png_store_buffer
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500435{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500436 struct png_store_buffer* prev; /* NOTE: stored in reverse order */
437 png_byte buffer[STORE_BUFFER_SIZE];
438} png_store_buffer;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500439
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500440#define FILE_NAME_SIZE 64
441
John Bowler9994f252011-05-15 18:52:39 -0500442typedef struct store_palette_entry /* record of a single palette entry */
443{
444 png_byte red;
445 png_byte green;
446 png_byte blue;
447 png_byte alpha;
448} store_palette_entry, store_palette[256];
449
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500450typedef struct png_store_file
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500451{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500452 struct png_store_file* next; /* as many as you like... */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500453 char name[FILE_NAME_SIZE];
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500454 png_uint_32 id; /* must be correct (see FILEID) */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500455 png_size_t datacount; /* In this (the last) buffer */
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500456 png_store_buffer data; /* Last buffer in file */
John Bowler9994f252011-05-15 18:52:39 -0500457 int npalette; /* Number of entries in palette */
458 store_palette_entry* palette; /* May be NULL */
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500459} png_store_file;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500460
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500461/* The following is a pool of memory allocated by a single libpng read or write
462 * operation.
463 */
464typedef struct store_pool
465{
466 struct png_store *store; /* Back pointer */
467 struct store_memory *list; /* List of allocated memory */
468 png_byte mark[4]; /* Before and after data */
469
470 /* Statistics for this run. */
471 png_alloc_size_t max; /* Maximum single allocation */
472 png_alloc_size_t current; /* Current allocation */
473 png_alloc_size_t limit; /* Highest current allocation */
474 png_alloc_size_t total; /* Total allocation */
475
476 /* Overall statistics (retained across successive runs). */
477 png_alloc_size_t max_max;
478 png_alloc_size_t max_limit;
479 png_alloc_size_t max_total;
480} store_pool;
481
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500482typedef struct png_store
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500483{
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500484 /* For cexcept.h exception handling - simply store one of these;
485 * the context is a self pointer but it may point to a different
486 * png_store (in fact it never does in this program.)
487 */
488 struct exception_context
489 exception_context;
490
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -0500491 unsigned int verbose :1;
492 unsigned int treat_warnings_as_errors :1;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500493 unsigned int expect_error :1;
494 unsigned int expect_warning :1;
495 unsigned int saw_warning :1;
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500496 unsigned int speed :1;
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500497 unsigned int progressive :1; /* use progressive read */
498 unsigned int validated :1; /* used as a temporary flag */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500499 int nerrors;
500 int nwarnings;
John Bowlerafea7d12011-01-28 06:38:14 -0600501 char test[128]; /* Name of test */
502 char error[256];
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500503
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500504 /* Read fields */
505 png_structp pread; /* Used to read a saved file */
506 png_infop piread;
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500507 png_store_file* current; /* Set when reading */
508 png_store_buffer* next; /* Set when reading */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500509 png_size_t readpos; /* Position in *next */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500510 png_byte* image; /* Buffer for reading interlaced images */
John Bowler9994f252011-05-15 18:52:39 -0500511 png_size_t cb_image; /* Size of this buffer */
512 png_size_t cb_row; /* Row size of the image(s) */
513 png_uint_32 image_h; /* Number of rows in a single image */
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500514 store_pool read_memory_pool;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500515
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500516 /* Write fields */
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500517 png_store_file* saved;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500518 png_structp pwrite; /* Used when writing a new file */
519 png_infop piwrite;
520 png_size_t writepos; /* Position in .new */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500521 char wname[FILE_NAME_SIZE];
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500522 png_store_buffer new; /* The end of the new PNG file being written. */
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500523 store_pool write_memory_pool;
John Bowler9994f252011-05-15 18:52:39 -0500524 store_palette_entry* palette;
525 int npalette;
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500526} png_store;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500527
528/* Initialization and cleanup */
529static void
John Bowler9994f252011-05-15 18:52:39 -0500530store_pool_mark(png_bytep mark)
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500531{
John Bowler9994f252011-05-15 18:52:39 -0500532 static png_uint_32 store_seed[2] = { 0x12345678, 1};
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500533
John Bowler9994f252011-05-15 18:52:39 -0500534 make_four_random_bytes(store_seed, mark);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500535}
536
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -0500537/* Use this for random 32 bit values; this function makes sure the result is
John Bowlerafea7d12011-01-28 06:38:14 -0600538 * non-zero.
539 */
540static png_uint_32
541random_32(void)
542{
543
544 for(;;)
545 {
546 png_byte mark[4];
547 png_uint_32 result;
548
549 store_pool_mark(mark);
550 result = png_get_uint_32(mark);
551
552 if (result != 0)
553 return result;
554 }
555}
556
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500557static void
558store_pool_init(png_store *ps, store_pool *pool)
559{
560 memset(pool, 0, sizeof *pool);
561
562 pool->store = ps;
563 pool->list = NULL;
564 pool->max = pool->current = pool->limit = pool->total = 0;
565 pool->max_max = pool->max_limit = pool->max_total = 0;
566 store_pool_mark(pool->mark);
567}
568
569static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500570store_init(png_store* ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500571{
572 memset(ps, 0, sizeof *ps);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500573 init_exception_context(&ps->exception_context);
574 store_pool_init(ps, &ps->read_memory_pool);
575 store_pool_init(ps, &ps->write_memory_pool);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500576 ps->verbose = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500577 ps->treat_warnings_as_errors = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500578 ps->expect_error = 0;
579 ps->expect_warning = 0;
580 ps->saw_warning = 0;
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500581 ps->speed = 0;
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500582 ps->progressive = 0;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500583 ps->validated = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500584 ps->nerrors = ps->nwarnings = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500585 ps->pread = NULL;
586 ps->piread = NULL;
587 ps->saved = ps->current = NULL;
588 ps->next = NULL;
589 ps->readpos = 0;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500590 ps->image = NULL;
591 ps->cb_image = 0;
John Bowler9994f252011-05-15 18:52:39 -0500592 ps->cb_row = 0;
593 ps->image_h = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500594 ps->pwrite = NULL;
595 ps->piwrite = NULL;
596 ps->writepos = 0;
597 ps->new.prev = NULL;
John Bowler9994f252011-05-15 18:52:39 -0500598 ps->palette = NULL;
599 ps->npalette = 0;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500600}
601
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500602static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500603store_freebuffer(png_store_buffer* psb)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500604{
605 if (psb->prev)
606 {
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500607 store_freebuffer(psb->prev);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500608 free(psb->prev);
609 psb->prev = NULL;
610 }
611}
612
613static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500614store_freenew(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500615{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500616 store_freebuffer(&ps->new);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500617 ps->writepos = 0;
John Bowler9994f252011-05-15 18:52:39 -0500618 if (ps->palette != NULL)
619 {
620 free(ps->palette);
621 ps->palette = NULL;
622 ps->npalette = 0;
623 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500624}
625
626static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500627store_storenew(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500628{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500629 png_store_buffer *pb;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500630
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500631 if (ps->writepos != STORE_BUFFER_SIZE)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500632 png_error(ps->pwrite, "invalid store call");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500633
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500634 pb = malloc(sizeof *pb);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500635
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500636 if (pb == NULL)
637 png_error(ps->pwrite, "store new: OOM");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500638
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500639 *pb = ps->new;
640 ps->new.prev = pb;
641 ps->writepos = 0;
642}
643
644static void
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500645store_freefile(png_store_file **ppf)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500646{
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500647 if (*ppf != NULL)
648 {
649 store_freefile(&(*ppf)->next);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500650
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500651 store_freebuffer(&(*ppf)->data);
652 (*ppf)->datacount = 0;
John Bowler9994f252011-05-15 18:52:39 -0500653 if ((*ppf)->palette != NULL)
654 {
655 free((*ppf)->palette);
656 (*ppf)->palette = NULL;
657 (*ppf)->npalette = 0;
658 }
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500659 free(*ppf);
660 *ppf = NULL;
661 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500662}
663
664/* Main interface to file storeage, after writing a new PNG file (see the API
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500665 * below) call store_storefile to store the result with the given name and id.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500666 */
667static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500668store_storefile(png_store *ps, png_uint_32 id)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500669{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500670 png_store_file *pf = malloc(sizeof *pf);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500671 if (pf == NULL)
672 png_error(ps->pwrite, "storefile: OOM");
673 safecat(pf->name, sizeof pf->name, 0, ps->wname);
674 pf->id = id;
675 pf->data = ps->new;
676 pf->datacount = ps->writepos;
677 ps->new.prev = NULL;
678 ps->writepos = 0;
John Bowler9994f252011-05-15 18:52:39 -0500679 pf->palette = ps->palette;
680 pf->npalette = ps->npalette;
681 ps->palette = 0;
682 ps->npalette = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500683
684 /* And save it. */
685 pf->next = ps->saved;
686 ps->saved = pf;
687}
688
689/* Generate an error message (in the given buffer) */
690static size_t
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500691store_message(png_store *ps, png_structp pp, char *buffer, size_t bufsize,
692 size_t pos, PNG_CONST char *msg)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500693{
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500694 if (pp != NULL && pp == ps->pread)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500695 {
696 /* Reading a file */
697 pos = safecat(buffer, bufsize, pos, "read: ");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500698
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500699 if (ps->current != NULL)
700 {
701 pos = safecat(buffer, bufsize, pos, ps->current->name);
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -0500702 pos = safecat(buffer, bufsize, pos, sep);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500703 }
704 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500705
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500706 else if (pp != NULL && pp == ps->pwrite)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500707 {
708 /* Writing a file */
709 pos = safecat(buffer, bufsize, pos, "write: ");
710 pos = safecat(buffer, bufsize, pos, ps->wname);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500711 pos = safecat(buffer, bufsize, pos, sep);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500712 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500713
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500714 else
715 {
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500716 /* Neither reading nor writing (or a memory error in struct delete) */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500717 pos = safecat(buffer, bufsize, pos, "pngvalid: ");
718 }
719
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500720 if (ps->test[0] != 0)
721 {
722 pos = safecat(buffer, bufsize, pos, ps->test);
723 pos = safecat(buffer, bufsize, pos, sep);
724 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500725 pos = safecat(buffer, bufsize, pos, msg);
726 return pos;
727}
728
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500729/* Log an error or warning - the relevant count is always incremented. */
730static void
731store_log(png_store* ps, png_structp pp, png_const_charp message, int is_error)
732{
733 /* The warning is copied to the error buffer if there are no errors and it is
734 * the first warning. The error is copied to the error buffer if it is the
735 * first error (overwriting any prior warnings).
736 */
737 if (is_error ? (ps->nerrors)++ == 0 :
738 (ps->nwarnings)++ == 0 && ps->nerrors == 0)
739 store_message(ps, pp, ps->error, sizeof ps->error, 0, message);
740
741 if (ps->verbose)
742 {
743 char buffer[256];
744 size_t pos;
745
746 if (is_error)
747 pos = safecat(buffer, sizeof buffer, 0, "error: ");
748 else
749 pos = safecat(buffer, sizeof buffer, 0, "warning: ");
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -0600750
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500751 store_message(ps, pp, buffer, sizeof buffer, pos, message);
752 fputs(buffer, stderr);
753 fputc('\n', stderr);
754 }
755}
756
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500757/* Functions to use as PNG callbacks. */
758static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500759store_error(png_structp pp, png_const_charp message) /* PNG_NORETURN */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500760{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500761 png_store *ps = png_get_error_ptr(pp);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500762
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500763 if (!ps->expect_error)
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600764 store_log(ps, pp, message, 1 /* error */);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500765
766 /* And finally throw an exception. */
767 {
768 struct exception_context *the_exception_context = &ps->exception_context;
769 Throw ps;
770 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500771}
772
773static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500774store_warning(png_structp pp, png_const_charp message)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500775{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500776 png_store *ps = png_get_error_ptr(pp);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500777
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500778 if (!ps->expect_warning)
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600779 store_log(ps, pp, message, 0 /* warning */);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500780 else
781 ps->saw_warning = 1;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500782}
783
John Bowler9994f252011-05-15 18:52:39 -0500784/* These somewhat odd functions are used when reading an image to ensure that
785 * the buffer is big enough, the png_structp is for errors.
786 */
787/* Return a single row from the correct image. */
788static png_bytep
789store_image_row(PNG_CONST png_store* ps, png_structp pp, int nImage,
790 png_uint_32 y)
791{
792 png_size_t coffset = (nImage * ps->image_h + y) * (ps->cb_row + 5) + 2;
793
794 if (ps->image == NULL)
795 png_error(pp, "no allocated image");
796
797 if (coffset + ps->cb_row + 3 > ps->cb_image)
798 png_error(pp, "image too small");
799
800 return ps->image + coffset;
801}
802
803static void
804store_image_free(png_store *ps, png_structp pp)
805{
806 if (ps->image != NULL)
807 {
808 png_bytep image = ps->image;
809
810 if (image[-1] != 0xed || image[ps->cb_image] != 0xfe)
811 {
812 if (pp != NULL)
813 png_error(pp, "png_store image overwrite (1)");
814 else
815 store_log(ps, NULL, "png_store image overwrite (2)", 1);
816 }
817
818 ps->image = NULL;
819 ps->cb_image = 0;
820 --image;
821 free(image);
822 }
823}
824
825static void
826store_ensure_image(png_store *ps, png_structp pp, int nImages, png_size_t cbRow,
827 png_uint_32 cRows)
828{
829 png_size_t cb = nImages * cRows * (cbRow + 5);
830
831 if (ps->cb_image < cb)
832 {
833 png_bytep image;
834
835 store_image_free(ps, pp);
836
837 /* The buffer is deliberately mis-aligned. */
838 image = malloc(cb+2);
839 if (image == NULL)
840 {
841 /* Called from the startup - ignore the error for the moment. */
842 if (pp == NULL)
843 return;
844
845 png_error(pp, "OOM allocating image buffer");
846 }
847
848 /* These magic tags are used to detect overwrites above. */
849 ++image;
850 image[-1] = 0xed;
851 image[cb] = 0xfe;
852
853 ps->image = image;
854 ps->cb_image = cb;
855 }
856
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -0500857 /* We have an adequate sized image; lay out the rows. There are 2 bytes at
858 * the start and three at the end of each (this ensures that the row alignment
John Bowler9994f252011-05-15 18:52:39 -0500859 * starts out odd - 2+1 and changes for larger images on each row.)
860 */
861 ps->cb_row = cbRow;
862 ps->image_h = cRows;
863
864 /* For error checking, the whole buffer is set to '1' - this matches what
865 * happens with the 'size' test images on write and also matches the unused
866 * bits in the test rows.
867 */
868 memset(ps->image, 0xff, cb);
869
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -0500870 /* Then put in the marks. */
John Bowler9994f252011-05-15 18:52:39 -0500871 while (--nImages >= 0)
872 {
873 png_uint_32 y;
874
875 for (y=0; y<cRows; ++y)
876 {
877 png_bytep row = store_image_row(ps, pp, nImages, y);
878
879 /* The markers: */
880 row[-2] = 190;
881 row[-1] = 239;
882 row[cbRow] = 222;
883 row[cbRow+1] = 173;
884 row[cbRow+2] = 17;
885 }
886 }
887}
888
889static void
890store_image_check(PNG_CONST png_store* ps, png_structp pp, int iImage)
891{
892 png_const_bytep image = ps->image;
893
894 if (image[-1] != 0xed || image[ps->cb_image] != 0xfe)
895 png_error(pp, "image overwrite");
896 else
897 {
898 png_size_t cbRow = ps->cb_row;
899 png_uint_32 rows = ps->image_h;
900
901 image += iImage * (cbRow+5) * ps->image_h;
902
903 image += 2; /* skip image first row markers */
904
905 while (rows-- > 0)
906 {
907 if (image[-2] != 190 || image[-1] != 239)
908 png_error(pp, "row start overwritten");
909
910 if (image[cbRow] != 222 || image[cbRow+1] != 173 ||
911 image[cbRow+2] != 17)
912 png_error(pp, "row end overwritten");
913
914 image += cbRow+5;
915 }
916 }
917}
918
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500919static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500920store_write(png_structp pp, png_bytep pb, png_size_t st)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500921{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500922 png_store *ps = png_get_io_ptr(pp);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500923
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500924 if (ps->pwrite != pp)
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500925 png_error(pp, "store state damaged");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500926
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500927 while (st > 0)
928 {
929 size_t cb;
930
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500931 if (ps->writepos >= STORE_BUFFER_SIZE)
932 store_storenew(ps);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500933
934 cb = st;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500935
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500936 if (cb > STORE_BUFFER_SIZE - ps->writepos)
937 cb = STORE_BUFFER_SIZE - ps->writepos;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500938
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500939 memcpy(ps->new.buffer + ps->writepos, pb, cb);
940 pb += cb;
941 st -= cb;
942 ps->writepos += cb;
943 }
944}
945
946static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500947store_flush(png_structp pp)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500948{
Glenn Randers-Pehrsond546f432010-12-04 20:41:36 -0600949 UNUSED(pp) /*DOES NOTHING*/
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500950}
951
952static size_t
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500953store_read_buffer_size(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500954{
955 /* Return the bytes available for read in the current buffer. */
956 if (ps->next != &ps->current->data)
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500957 return STORE_BUFFER_SIZE;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500958
959 return ps->current->datacount;
960}
961
John Bowler4a12f4a2011-04-17 18:34:22 -0500962#ifdef PNG_READ_TRANSFORMS_SUPPORTED
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500963/* Return total bytes available for read. */
964static size_t
965store_read_buffer_avail(png_store *ps)
966{
967 if (ps->current != NULL && ps->next != NULL)
968 {
969 png_store_buffer *next = &ps->current->data;
970 size_t cbAvail = ps->current->datacount;
971
972 while (next != ps->next && next != NULL)
973 {
974 next = next->prev;
975 cbAvail += STORE_BUFFER_SIZE;
976 }
977
978 if (next != ps->next)
979 png_error(ps->pread, "buffer read error");
980
981 if (cbAvail > ps->readpos)
982 return cbAvail - ps->readpos;
983 }
984
985 return 0;
986}
John Bowler4a12f4a2011-04-17 18:34:22 -0500987#endif
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500988
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500989static int
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500990store_read_buffer_next(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500991{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500992 png_store_buffer *pbOld = ps->next;
993 png_store_buffer *pbNew = &ps->current->data;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500994 if (pbOld != pbNew)
995 {
996 while (pbNew != NULL && pbNew->prev != pbOld)
997 pbNew = pbNew->prev;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500998
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500999 if (pbNew != NULL)
1000 {
1001 ps->next = pbNew;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001002 ps->readpos = 0;
1003 return 1;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001004 }
1005
1006 png_error(ps->pread, "buffer lost");
1007 }
1008
1009 return 0; /* EOF or error */
1010}
1011
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001012/* Need separate implementation and callback to allow use of the same code
1013 * during progressive read, where the io_ptr is set internally by libpng.
1014 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001015static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001016store_read_imp(png_store *ps, png_bytep pb, png_size_t st)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001017{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001018 if (ps->current == NULL || ps->next == NULL)
1019 png_error(ps->pread, "store state damaged");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001020
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001021 while (st > 0)
1022 {
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001023 size_t cbAvail = store_read_buffer_size(ps) - ps->readpos;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001024
1025 if (cbAvail > 0)
1026 {
1027 if (cbAvail > st) cbAvail = st;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001028 memcpy(pb, ps->next->buffer + ps->readpos, cbAvail);
1029 st -= cbAvail;
1030 pb += cbAvail;
1031 ps->readpos += cbAvail;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001032 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001033
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001034 else if (!store_read_buffer_next(ps))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001035 png_error(ps->pread, "read beyond end of file");
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001036 }
1037}
1038
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001039static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001040store_read(png_structp pp, png_bytep pb, png_size_t st)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001041{
1042 png_store *ps = png_get_io_ptr(pp);
1043
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001044 if (ps == NULL || ps->pread != pp)
1045 png_error(pp, "bad store read call");
1046
1047 store_read_imp(ps, pb, st);
1048}
1049
1050static void
1051store_progressive_read(png_store *ps, png_structp pp, png_infop pi)
1052{
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001053 /* Notice that a call to store_read will cause this function to fail because
1054 * readpos will be set.
1055 */
1056 if (ps->pread != pp || ps->current == NULL || ps->next == NULL)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001057 png_error(pp, "store state damaged (progressive)");
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001058
1059 do
1060 {
1061 if (ps->readpos != 0)
1062 png_error(pp, "store_read called during progressive read");
1063
1064 png_process_data(pp, pi, ps->next->buffer, store_read_buffer_size(ps));
1065 }
1066 while (store_read_buffer_next(ps));
1067}
1068
John Bowler9994f252011-05-15 18:52:39 -05001069/* The caller must fill this in: */
1070static store_palette_entry *
1071store_write_palette(png_store *ps, int npalette)
1072{
1073 if (ps->pwrite == NULL)
1074 store_log(ps, NULL, "attempt to write palette without write stream", 1);
1075
1076 if (ps->palette != NULL)
1077 png_error(ps->pwrite, "multiple store_write_palette calls");
1078
1079 /* This function can only return NULL if called with '0'! */
1080 if (npalette > 0)
1081 {
1082 ps->palette = malloc(npalette * sizeof *ps->palette);
1083
1084 if (ps->palette == NULL)
1085 png_error(ps->pwrite, "store new palette: OOM");
1086
1087 ps->npalette = npalette;
1088 }
1089
1090 return ps->palette;
1091}
1092
1093static store_palette_entry *
1094store_current_palette(png_store *ps, int *npalette)
1095{
1096 /* This is an internal error (the call has been made outside a read
1097 * operation.)
1098 */
1099 if (ps->current == NULL)
1100 store_log(ps, ps->pread, "no current stream for palette", 1);
1101
1102 /* The result may be null if there is no palette. */
1103 *npalette = ps->current->npalette;
1104 return ps->current->palette;
1105}
1106
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001107/***************************** MEMORY MANAGEMENT*** ***************************/
1108/* A store_memory is simply the header for an allocated block of memory. The
1109 * pointer returned to libpng is just after the end of the header block, the
1110 * allocated memory is followed by a second copy of the 'mark'.
1111 */
1112typedef struct store_memory
1113{
1114 store_pool *pool; /* Originating pool */
1115 struct store_memory *next; /* Singly linked list */
1116 png_alloc_size_t size; /* Size of memory allocated */
1117 png_byte mark[4]; /* ID marker */
1118} store_memory;
1119
1120/* Handle a fatal error in memory allocation. This calls png_error if the
1121 * libpng struct is non-NULL, else it outputs a message and returns. This means
1122 * that a memory problem while libpng is running will abort (png_error) the
1123 * handling of particular file while one in cleanup (after the destroy of the
1124 * struct has returned) will simply keep going and free (or attempt to free)
1125 * all the memory.
1126 */
1127static void
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001128store_pool_error(png_store *ps, png_structp pp, PNG_CONST char *msg)
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001129{
1130 if (pp != NULL)
1131 png_error(pp, msg);
1132
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001133 /* Else we have to do it ourselves. png_error eventually calls store_log,
1134 * above. store_log accepts a NULL png_structp - it just changes what gets
1135 * output by store_message.
1136 */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001137 store_log(ps, pp, msg, 1 /* error */);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001138}
1139
1140static void
1141store_memory_free(png_structp pp, store_pool *pool, store_memory *memory)
1142{
1143 /* Note that pp may be NULL (see store_pool_delete below), the caller has
1144 * found 'memory' in pool->list *and* unlinked this entry, so this is a valid
1145 * pointer (for sure), but the contents may have been trashed.
1146 */
1147 if (memory->pool != pool)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001148 store_pool_error(pool->store, pp, "memory corrupted (pool)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001149
1150 else if (memcmp(memory->mark, pool->mark, sizeof memory->mark) != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001151 store_pool_error(pool->store, pp, "memory corrupted (start)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001152
1153 /* It should be safe to read the size field now. */
1154 else
1155 {
1156 png_alloc_size_t cb = memory->size;
1157
1158 if (cb > pool->max)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001159 store_pool_error(pool->store, pp, "memory corrupted (size)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001160
1161 else if (memcmp((png_bytep)(memory+1)+cb, pool->mark, sizeof pool->mark)
1162 != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001163 store_pool_error(pool->store, pp, "memory corrupted (end)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001164
1165 /* Finally give the library a chance to find problems too: */
1166 else
1167 {
1168 pool->current -= cb;
1169 free(memory);
1170 }
1171 }
1172}
1173
1174static void
1175store_pool_delete(png_store *ps, store_pool *pool)
1176{
1177 if (pool->list != NULL)
1178 {
1179 fprintf(stderr, "%s: %s %s: memory lost (list follows):\n", ps->test,
1180 pool == &ps->read_memory_pool ? "read" : "write",
1181 pool == &ps->read_memory_pool ? (ps->current != NULL ?
1182 ps->current->name : "unknown file") : ps->wname);
1183 ++ps->nerrors;
1184
1185 do
1186 {
1187 store_memory *next = pool->list;
1188 pool->list = next->next;
1189 next->next = NULL;
1190
Glenn Randers-Pehrson9a75d992010-10-08 16:27:14 -05001191 fprintf(stderr, "\t%lu bytes @ %p\n",
John Bowler9c693602011-02-12 08:58:21 -06001192 (unsigned long)next->size, (PNG_CONST void*)(next+1));
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001193 /* The NULL means this will always return, even if the memory is
1194 * corrupted.
1195 */
1196 store_memory_free(NULL, pool, next);
1197 }
1198 while (pool->list != NULL);
1199 }
1200
1201 /* And reset the other fields too for the next time. */
1202 if (pool->max > pool->max_max) pool->max_max = pool->max;
1203 pool->max = 0;
1204 if (pool->current != 0) /* unexpected internal error */
1205 fprintf(stderr, "%s: %s %s: memory counter mismatch (internal error)\n",
1206 ps->test, pool == &ps->read_memory_pool ? "read" : "write",
1207 pool == &ps->read_memory_pool ? (ps->current != NULL ?
1208 ps->current->name : "unknown file") : ps->wname);
1209 pool->current = 0;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001210
1211 if (pool->limit > pool->max_limit)
1212 pool->max_limit = pool->limit;
1213
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001214 pool->limit = 0;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001215
1216 if (pool->total > pool->max_total)
1217 pool->max_total = pool->total;
1218
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001219 pool->total = 0;
1220
1221 /* Get a new mark too. */
1222 store_pool_mark(pool->mark);
1223}
1224
1225/* The memory callbacks: */
1226static png_voidp
1227store_malloc(png_structp pp, png_alloc_size_t cb)
1228{
1229 store_pool *pool = png_get_mem_ptr(pp);
1230 store_memory *new = malloc(cb + (sizeof *new) + (sizeof pool->mark));
1231
1232 if (new != NULL)
1233 {
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001234 if (cb > pool->max)
1235 pool->max = cb;
1236
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001237 pool->current += cb;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001238
1239 if (pool->current > pool->limit)
1240 pool->limit = pool->current;
1241
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001242 pool->total += cb;
1243
1244 new->size = cb;
1245 memcpy(new->mark, pool->mark, sizeof new->mark);
1246 memcpy((png_byte*)(new+1) + cb, pool->mark, sizeof pool->mark);
1247 new->pool = pool;
1248 new->next = pool->list;
1249 pool->list = new;
1250 ++new;
1251 }
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001252
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001253 else
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001254 store_pool_error(pool->store, pp, "out of memory");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001255
1256 return new;
1257}
1258
1259static void
1260store_free(png_structp pp, png_voidp memory)
1261{
1262 store_pool *pool = png_get_mem_ptr(pp);
1263 store_memory *this = memory, **test;
1264
1265 /* First check that this 'memory' really is valid memory - it must be in the
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001266 * pool list. If it is, use the shared memory_free function to free it.
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001267 */
1268 --this;
1269 for (test = &pool->list; *test != this; test = &(*test)->next)
1270 {
1271 if (*test == NULL)
1272 {
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001273 store_pool_error(pool->store, pp, "bad pointer to free");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001274 return;
1275 }
1276 }
1277
1278 /* Unlink this entry, *test == this. */
1279 *test = this->next;
1280 this->next = NULL;
1281 store_memory_free(pp, pool, this);
1282}
1283
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001284/* Setup functions. */
1285/* Cleanup when aborting a write or after storing the new file. */
1286static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001287store_write_reset(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001288{
1289 if (ps->pwrite != NULL)
1290 {
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001291 anon_context(ps);
1292
1293 Try
1294 png_destroy_write_struct(&ps->pwrite, &ps->piwrite);
1295
1296 Catch_anonymous
1297 {
1298 /* memory corruption: continue. */
1299 }
1300
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001301 ps->pwrite = NULL;
1302 ps->piwrite = NULL;
1303 }
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001304
1305 /* And make sure that all the memory has been freed - this will output
1306 * spurious errors in the case of memory corruption above, but this is safe.
1307 */
1308 store_pool_delete(ps, &ps->write_memory_pool);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001309
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001310 store_freenew(ps);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001311}
1312
1313/* The following is the main write function, it returns a png_struct and,
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001314 * optionally, a png_info suitable for writiing a new PNG file. Use
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001315 * store_storefile above to record this file after it has been written. The
1316 * returned libpng structures as destroyed by store_write_reset above.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001317 */
1318static png_structp
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001319set_store_for_write(png_store *ps, png_infopp ppi,
1320 PNG_CONST char * volatile name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001321{
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001322 anon_context(ps);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001323
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001324 Try
1325 {
1326 if (ps->pwrite != NULL)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001327 png_error(ps->pwrite, "write store already in use");
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001328
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001329 store_write_reset(ps);
1330 safecat(ps->wname, sizeof ps->wname, 0, name);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001331
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001332 /* Don't do the slow memory checks if doing a speed test. */
1333 if (ps->speed)
1334 ps->pwrite = png_create_write_struct(PNG_LIBPNG_VER_STRING,
1335 ps, store_error, store_warning);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001336
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001337 else
1338 ps->pwrite = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
1339 ps, store_error, store_warning, &ps->write_memory_pool,
1340 store_malloc, store_free);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001341
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001342 png_set_write_fn(ps->pwrite, ps, store_write, store_flush);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001343
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001344 if (ppi != NULL)
1345 *ppi = ps->piwrite = png_create_info_struct(ps->pwrite);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001346 }
1347
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001348 Catch_anonymous
1349 return NULL;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001350
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001351 return ps->pwrite;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001352}
1353
Glenn Randers-Pehrsonbc363ec2010-10-12 21:17:00 -05001354/* Cleanup when finished reading (either due to error or in the success case).
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001355 */
1356static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001357store_read_reset(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001358{
1359 if (ps->pread != NULL)
1360 {
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001361 anon_context(ps);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001362
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001363 Try
1364 png_destroy_read_struct(&ps->pread, &ps->piread, NULL);
1365
1366 Catch_anonymous
1367 {
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001368 /* error already output: continue */
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001369 }
1370
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001371 ps->pread = NULL;
1372 ps->piread = NULL;
1373 }
1374
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001375 /* Always do this to be safe. */
1376 store_pool_delete(ps, &ps->read_memory_pool);
1377
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001378 ps->current = NULL;
1379 ps->next = NULL;
1380 ps->readpos = 0;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001381 ps->validated = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001382}
1383
1384static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001385store_read_set(png_store *ps, png_uint_32 id)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001386{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001387 png_store_file *pf = ps->saved;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001388
1389 while (pf != NULL)
1390 {
1391 if (pf->id == id)
1392 {
1393 ps->current = pf;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001394 ps->next = NULL;
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001395 store_read_buffer_next(ps);
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001396 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001397 }
1398
1399 pf = pf->next;
1400 }
1401
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001402 {
1403 size_t pos;
1404 char msg[FILE_NAME_SIZE+64];
1405
1406 pos = standard_name_from_id(msg, sizeof msg, 0, id);
1407 pos = safecat(msg, sizeof msg, pos, ": file not found");
1408 png_error(ps->pread, msg);
1409 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001410}
1411
1412/* The main interface for reading a saved file - pass the id number of the file
1413 * to retrieve. Ids must be unique or the earlier file will be hidden. The API
1414 * returns a png_struct and, optionally, a png_info. Both of these will be
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001415 * destroyed by store_read_reset above.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001416 */
1417static png_structp
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001418set_store_for_read(png_store *ps, png_infopp ppi, png_uint_32 id,
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001419 PNG_CONST char *name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001420{
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001421 /* Set the name for png_error */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001422 safecat(ps->test, sizeof ps->test, 0, name);
1423
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001424 if (ps->pread != NULL)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001425 png_error(ps->pread, "read store already in use");
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001426
1427 store_read_reset(ps);
1428
1429 /* Both the create APIs can return NULL if used in their default mode
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001430 * (because there is no other way of handling an error because the jmp_buf
1431 * by default is stored in png_struct and that has not been allocated!)
1432 * However, given that store_error works correctly in these circumstances
1433 * we don't ever expect NULL in this program.
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001434 */
1435 if (ps->speed)
1436 ps->pread = png_create_read_struct(PNG_LIBPNG_VER_STRING, ps,
1437 store_error, store_warning);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001438
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001439 else
1440 ps->pread = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, ps,
1441 store_error, store_warning, &ps->read_memory_pool, store_malloc,
1442 store_free);
1443
1444 if (ps->pread == NULL)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001445 {
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001446 struct exception_context *the_exception_context = &ps->exception_context;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001447
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001448 store_log(ps, NULL, "png_create_read_struct returned NULL (unexpected)",
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001449 1 /*error*/);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001450
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001451 Throw ps;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001452 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001453
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001454 store_read_set(ps, id);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001455
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001456 if (ppi != NULL)
1457 *ppi = ps->piread = png_create_info_struct(ps->pread);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001458
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001459 return ps->pread;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001460}
1461
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001462/* The overall cleanup of a store simply calls the above then removes all the
1463 * saved files. This does not delete the store itself.
1464 */
1465static void
1466store_delete(png_store *ps)
1467{
1468 store_write_reset(ps);
1469 store_read_reset(ps);
1470 store_freefile(&ps->saved);
John Bowler9994f252011-05-15 18:52:39 -05001471 store_image_free(ps, NULL);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001472}
1473
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001474/*********************** PNG FILE MODIFICATION ON READ ************************/
1475/* Files may be modified on read. The following structure contains a complete
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001476 * png_store together with extra members to handle modification and a special
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001477 * read callback for libpng. To use this the 'modifications' field must be set
1478 * to a list of png_modification structures that actually perform the
1479 * modification, otherwise a png_modifier is functionally equivalent to a
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001480 * png_store. There is a special read function, set_modifier_for_read, which
1481 * replaces set_store_for_read.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001482 */
1483typedef struct png_modifier
1484{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001485 png_store this; /* I am a png_store */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001486 struct png_modification *modifications; /* Changes to make */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001487
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001488 enum modifier_state
1489 {
1490 modifier_start, /* Initial value */
1491 modifier_signature, /* Have a signature */
1492 modifier_IHDR /* Have an IHDR */
1493 } state; /* My state */
1494
1495 /* Information from IHDR: */
1496 png_byte bit_depth; /* From IHDR */
1497 png_byte colour_type; /* From IHDR */
1498
1499 /* While handling PLTE, IDAT and IEND these chunks may be pended to allow
1500 * other chunks to be inserted.
1501 */
1502 png_uint_32 pending_len;
1503 png_uint_32 pending_chunk;
1504
1505 /* Test values */
1506 double *gammas;
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001507 unsigned int ngammas;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001508
1509 /* Lowest sbit to test (libpng fails for sbit < 8) */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001510 png_byte sbitlow;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001511
1512 /* Error control - these are the limits on errors accepted by the gamma tests
1513 * below.
1514 */
1515 double maxout8; /* Maximum output value error */
Glenn Randers-Pehrson21b4b332010-08-18 07:12:38 -05001516 double maxabs8; /* Absolute sample error 0..1 */
John Bowlerd273ad22011-05-07 21:00:28 -05001517 double maxcalc8; /* Absolute sample error 0..1 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001518 double maxpc8; /* Percentage sample error 0..100% */
1519 double maxout16; /* Maximum output value error */
1520 double maxabs16; /* Absolute sample error 0..1 */
John Bowlerd273ad22011-05-07 21:00:28 -05001521 double maxcalc16;/* Absolute sample error 0..1 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001522 double maxpc16; /* Percentage sample error 0..100% */
1523
1524 /* Logged 8 and 16 bit errors ('output' values): */
1525 double error_gray_2;
1526 double error_gray_4;
1527 double error_gray_8;
1528 double error_gray_16;
1529 double error_color_8;
1530 double error_color_16;
John Bowler1921e6d2011-05-16 20:57:54 -05001531 double error_indexed;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001532
1533 /* Flags: */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001534 /* Whether or not to interlace. */
1535 int interlace_type :9; /* int, but must store '1' */
1536
John Bowlerb54498e2010-12-08 16:26:21 -06001537 /* Run the standard tests? */
1538 unsigned int test_standard :1;
1539
John Bowler660c6e42010-12-19 06:22:23 -06001540 /* Run the odd-sized image and interlace read/write tests? */
1541 unsigned int test_size :1;
1542
John Bowlerf21a0d02011-01-23 23:55:19 -06001543 /* Run tests on reading with a combiniation of transforms, */
1544 unsigned int test_transform :1;
1545
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001546 /* When to use the use_input_precision option: */
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001547 unsigned int use_input_precision :1;
1548 unsigned int use_input_precision_sbit :1;
1549 unsigned int use_input_precision_16to8 :1;
John Bowlerb54498e2010-12-08 16:26:21 -06001550
John Bowler5441e182011-05-18 18:57:12 -05001551 /* If set assume that the calculation bit depth is set by the input
1552 * precision, not the output precision.
1553 */
1554 unsigned int calculations_use_input_precision :1;
1555
1556 /* If set assume that the calculations are done in 16 bits even if both input
1557 * and output are 8 bit or less.
1558 */
1559 unsigned int assume_16_bit_calculations :1;
John Bowlerd273ad22011-05-07 21:00:28 -05001560
John Bowlerb54498e2010-12-08 16:26:21 -06001561 /* Which gamma tests to run: */
John Bowlerf21a0d02011-01-23 23:55:19 -06001562 unsigned int test_gamma_threshold :1;
1563 unsigned int test_gamma_transform :1; /* main tests */
1564 unsigned int test_gamma_sbit :1;
1565 unsigned int test_gamma_strip16 :1;
John Bowlerd273ad22011-05-07 21:00:28 -05001566 unsigned int test_gamma_background :1;
1567 unsigned int test_gamma_alpha_mode :1;
1568 unsigned int test_gamma_expand16 :1;
John Bowlerb54498e2010-12-08 16:26:21 -06001569
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001570 unsigned int log :1; /* Log max error */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001571
1572 /* Buffer information, the buffer size limits the size of the chunks that can
1573 * be modified - they must fit (including header and CRC) into the buffer!
1574 */
1575 size_t flush; /* Count of bytes to flush */
1576 size_t buffer_count; /* Bytes in buffer */
1577 size_t buffer_position; /* Position in buffer */
1578 png_byte buffer[1024];
1579} png_modifier;
1580
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001581/* This returns true if the test should be stopped now because it has already
1582 * failed and it is running silently.
1583 */
1584static int fail(png_modifier *pm)
1585{
1586 return !pm->log && !pm->this.verbose && (pm->this.nerrors > 0 ||
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001587 (pm->this.treat_warnings_as_errors && pm->this.nwarnings > 0));
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001588}
1589
1590static void
1591modifier_init(png_modifier *pm)
1592{
1593 memset(pm, 0, sizeof *pm);
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001594 store_init(&pm->this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001595 pm->modifications = NULL;
1596 pm->state = modifier_start;
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001597 pm->sbitlow = 1U;
John Bowlerd273ad22011-05-07 21:00:28 -05001598 pm->ngammas = 0;
1599 pm->gammas = 0;
1600 pm->maxout8 = pm->maxpc8 = pm->maxabs8 = pm->maxcalc8 = 0;
1601 pm->maxout16 = pm->maxpc16 = pm->maxabs16 = pm->maxcalc16 = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001602 pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0;
1603 pm->error_gray_16 = pm->error_color_8 = pm->error_color_16 = 0;
John Bowler1921e6d2011-05-16 20:57:54 -05001604 pm->error_indexed = 0;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001605 pm->interlace_type = PNG_INTERLACE_NONE;
John Bowlerafea7d12011-01-28 06:38:14 -06001606 pm->test_standard = 0;
John Bowler660c6e42010-12-19 06:22:23 -06001607 pm->test_size = 0;
John Bowlerafea7d12011-01-28 06:38:14 -06001608 pm->test_transform = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001609 pm->use_input_precision = 0;
1610 pm->use_input_precision_sbit = 0;
1611 pm->use_input_precision_16to8 = 0;
John Bowler5441e182011-05-18 18:57:12 -05001612 pm->calculations_use_input_precision = 0;
John Bowlerafea7d12011-01-28 06:38:14 -06001613 pm->test_gamma_threshold = 0;
1614 pm->test_gamma_transform = 0;
1615 pm->test_gamma_sbit = 0;
1616 pm->test_gamma_strip16 = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05001617 pm->test_gamma_background = 0;
1618 pm->test_gamma_alpha_mode = 0;
1619 pm->test_gamma_expand16 = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001620 pm->log = 0;
1621
1622 /* Rely on the memset for all the other fields - there are no pointers */
1623}
1624
John Bowler4a12f4a2011-04-17 18:34:22 -05001625#ifdef PNG_READ_TRANSFORMS_SUPPORTED
John Bowler5441e182011-05-18 18:57:12 -05001626/* If pm->calculations_use_input_precision is set then operations will happen
1627 * with only 8 bit precision unless both the input and output bit depth are 16.
1628 *
1629 * If pm->assume_16_bit_calculations is set then even 8 bit calculations use 16
1630 * bit precision. This only affects those of the following limits that pertain
1631 * to a calculation - not a digitization operation!
1632 */
1633static double abserr(png_modifier *pm, int in_depth, int out_depth)
John Bowler4a12f4a2011-04-17 18:34:22 -05001634{
John Bowler5441e182011-05-18 18:57:12 -05001635 /* Absolute error permitted in linear values - affected by the bit depth of
1636 * the calculations.
John Bowlerd273ad22011-05-07 21:00:28 -05001637 */
John Bowler5441e182011-05-18 18:57:12 -05001638 if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 ||
1639 !pm->calculations_use_input_precision)))
1640 return pm->maxabs16;
John Bowlerd273ad22011-05-07 21:00:28 -05001641 else
John Bowler5441e182011-05-18 18:57:12 -05001642 return pm->maxabs8;
John Bowlerd273ad22011-05-07 21:00:28 -05001643}
1644
John Bowler5441e182011-05-18 18:57:12 -05001645static double calcerr(png_modifier *pm, int in_depth, int out_depth)
John Bowler4a12f4a2011-04-17 18:34:22 -05001646{
John Bowler5441e182011-05-18 18:57:12 -05001647 /* Error in the linear composition arithmetic - only relevant when
1648 * composition actually happens (0 < alpha < 1).
1649 */
1650 if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 ||
1651 !pm->calculations_use_input_precision)))
1652 return pm->maxcalc16;
1653 else
1654 return pm->maxcalc8;
John Bowler4a12f4a2011-04-17 18:34:22 -05001655}
1656
John Bowler5441e182011-05-18 18:57:12 -05001657static double pcerr(png_modifier *pm, int in_depth, int out_depth)
1658{
1659 /* Percentage error permitted in the linear values. Note that the specified
1660 * value is a percentage but this routine returns a simple number.
1661 */
1662 if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 ||
1663 !pm->calculations_use_input_precision)))
1664 return pm->maxpc16 * .01;
1665 else
1666 return pm->maxpc8 * .01;
1667}
1668
1669/* Output error - the error in the encoded value. This is determined by the
1670 * digitization of the output so can be +/-0.5 in the actual output value. In
1671 * the expand_16 case with the current code in libpng the expand happens after
1672 * all the calculations are done in 8 bit arithmetic, so even though the output
1673 * depth is 16 the output error is determined by the 8 bit calculation.
1674 *
1675 * This limit is not determined by the bit depth of internal calculations.
1676 *
1677 * The specified parameter does *not* include the base .5 digitization error but
1678 * it is added here.
1679 */
1680static double outerr(png_modifier *pm, int in_depth, int out_depth)
John Bowler4a12f4a2011-04-17 18:34:22 -05001681{
1682 /* There is a serious error in the 2 and 4 bit grayscale transform because
1683 * the gamma table value (8 bits) is simply shifted, not rounded, so the
1684 * error in 4 bit greyscale gamma is up to the value below. This is a hack
1685 * to allow pngvalid to succeed:
John Bowler5441e182011-05-18 18:57:12 -05001686 *
1687 * TODO: fix this in libpng
John Bowler4a12f4a2011-04-17 18:34:22 -05001688 */
John Bowler5441e182011-05-18 18:57:12 -05001689 if (out_depth == 2)
John Bowler4a12f4a2011-04-17 18:34:22 -05001690 return .73182-.5;
1691
John Bowler5441e182011-05-18 18:57:12 -05001692 if (out_depth == 4)
John Bowler4a12f4a2011-04-17 18:34:22 -05001693 return .90644-.5;
1694
John Bowler5441e182011-05-18 18:57:12 -05001695 if (out_depth == 16 && (in_depth == 16 ||
1696 !pm->calculations_use_input_precision))
1697 return pm->maxout16;
John Bowler4a12f4a2011-04-17 18:34:22 -05001698
John Bowler5441e182011-05-18 18:57:12 -05001699 /* This is the case where the value was calculated at 8-bit precision then
1700 * scaled to 16 bits.
1701 */
1702 else if (out_depth == 16)
1703 return pm->maxout8 * 257;
1704
1705 else
1706 return pm->maxout8;
1707}
1708
1709/* This complements the above by providing the appropriate quantization for the
1710 * final value. Normally this would just be quantization to an integral value,
1711 * but in the 8 bit calculation case it's actually quantization to a multiple of
1712 * 257!
1713 */
1714static int output_quantization_factor(png_modifier *pm, int in_depth,
1715 int out_depth)
1716{
1717 if (out_depth == 16 && in_depth != 16
1718 && pm->calculations_use_input_precision)
1719 return 257;
1720 else
1721 return 1;
John Bowler4a12f4a2011-04-17 18:34:22 -05001722}
1723
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05001724/* One modification structure must be provided for each chunk to be modified (in
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001725 * fact more than one can be provided if multiple separate changes are desired
1726 * for a single chunk.) Modifications include adding a new chunk when a
1727 * suitable chunk does not exist.
1728 *
1729 * The caller of modify_fn will reset the CRC of the chunk and record 'modified'
1730 * or 'added' as appropriate if the modify_fn returns 1 (true). If the
1731 * modify_fn is NULL the chunk is simply removed.
1732 */
1733typedef struct png_modification
1734{
1735 struct png_modification *next;
1736 png_uint_32 chunk;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001737
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001738 /* If the following is NULL all matching chunks will be removed: */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001739 int (*modify_fn)(struct png_modifier *pm,
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001740 struct png_modification *me, int add);
1741
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001742 /* If the following is set to PLTE, IDAT or IEND and the chunk has not been
1743 * found and modified (and there is a modify_fn) the modify_fn will be called
1744 * to add the chunk before the relevant chunk.
1745 */
1746 png_uint_32 add;
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05001747 unsigned int modified :1; /* Chunk was modified */
1748 unsigned int added :1; /* Chunk was added */
1749 unsigned int removed :1; /* Chunk was removed */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001750} png_modification;
1751
1752static void modification_reset(png_modification *pmm)
1753{
1754 if (pmm != NULL)
1755 {
1756 pmm->modified = 0;
1757 pmm->added = 0;
1758 pmm->removed = 0;
1759 modification_reset(pmm->next);
1760 }
1761}
1762
1763static void
1764modification_init(png_modification *pmm)
1765{
1766 memset(pmm, 0, sizeof *pmm);
1767 pmm->next = NULL;
1768 pmm->chunk = 0;
1769 pmm->modify_fn = NULL;
1770 pmm->add = 0;
1771 modification_reset(pmm);
1772}
1773
1774static void
1775modifier_reset(png_modifier *pm)
1776{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001777 store_read_reset(&pm->this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001778 pm->modifications = NULL;
1779 pm->state = modifier_start;
1780 pm->bit_depth = pm->colour_type = 0;
1781 pm->pending_len = pm->pending_chunk = 0;
1782 pm->flush = pm->buffer_count = pm->buffer_position = 0;
1783}
1784
1785/* Convenience macros. */
1786#define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
1787#define CHUNK_IHDR CHUNK(73,72,68,82)
1788#define CHUNK_PLTE CHUNK(80,76,84,69)
1789#define CHUNK_IDAT CHUNK(73,68,65,84)
1790#define CHUNK_IEND CHUNK(73,69,78,68)
1791#define CHUNK_cHRM CHUNK(99,72,82,77)
1792#define CHUNK_gAMA CHUNK(103,65,77,65)
1793#define CHUNK_sBIT CHUNK(115,66,73,84)
1794#define CHUNK_sRGB CHUNK(115,82,71,66)
1795
1796/* The guts of modification are performed during a read. */
1797static void
1798modifier_crc(png_bytep buffer)
1799{
1800 /* Recalculate the chunk CRC - a complete chunk must be in
1801 * the buffer, at the start.
1802 */
1803 uInt datalen = png_get_uint_32(buffer);
1804 png_save_uint_32(buffer+datalen+8, crc32(0L, buffer+4, datalen+4));
1805}
1806
1807static void
1808modifier_setbuffer(png_modifier *pm)
1809{
1810 modifier_crc(pm->buffer);
1811 pm->buffer_count = png_get_uint_32(pm->buffer)+12;
1812 pm->buffer_position = 0;
1813}
1814
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001815/* Separate the callback into the actual implementation (which is passed the
1816 * png_modifier explicitly) and the callback, which gets the modifier from the
1817 * png_struct.
1818 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001819static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001820modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001821{
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001822 while (st > 0)
1823 {
1824 size_t cb;
1825 png_uint_32 len, chunk;
1826 png_modification *mod;
1827
1828 if (pm->buffer_position >= pm->buffer_count) switch (pm->state)
1829 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001830 static png_byte sign[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
1831 case modifier_start:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001832 store_read_imp(&pm->this, pm->buffer, 8); /* size of signature. */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001833 pm->buffer_count = 8;
1834 pm->buffer_position = 0;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001835
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001836 if (memcmp(pm->buffer, sign, 8) != 0)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001837 png_error(pm->this.pread, "invalid PNG file signature");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001838 pm->state = modifier_signature;
1839 break;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001840
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001841 case modifier_signature:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001842 store_read_imp(&pm->this, pm->buffer, 13+12); /* size of IHDR */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001843 pm->buffer_count = 13+12;
1844 pm->buffer_position = 0;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001845
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001846 if (png_get_uint_32(pm->buffer) != 13 ||
1847 png_get_uint_32(pm->buffer+4) != CHUNK_IHDR)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001848 png_error(pm->this.pread, "invalid IHDR");
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001849
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001850 /* Check the list of modifiers for modifications to the IHDR. */
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001851 mod = pm->modifications;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001852 while (mod != NULL)
1853 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001854 if (mod->chunk == CHUNK_IHDR && mod->modify_fn &&
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001855 (*mod->modify_fn)(pm, mod, 0))
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001856 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001857 mod->modified = 1;
1858 modifier_setbuffer(pm);
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001859 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001860
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001861 /* Ignore removal or add if IHDR! */
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001862 mod = mod->next;
1863 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001864
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001865 /* Cache information from the IHDR (the modified one.) */
1866 pm->bit_depth = pm->buffer[8+8];
1867 pm->colour_type = pm->buffer[8+8+1];
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001868
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001869 pm->state = modifier_IHDR;
1870 pm->flush = 0;
1871 break;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001872
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001873 case modifier_IHDR:
1874 default:
1875 /* Read a new chunk and process it until we see PLTE, IDAT or
1876 * IEND. 'flush' indicates that there is still some data to
1877 * output from the preceding chunk.
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001878 */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001879 if ((cb = pm->flush) > 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001880 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001881 if (cb > st) cb = st;
1882 pm->flush -= cb;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001883 store_read_imp(&pm->this, pb, cb);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001884 pb += cb;
1885 st -= cb;
John Bowlerd2f0bc22011-06-11 06:42:06 -05001886 if (st == 0) return;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001887 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001888
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001889 /* No more bytes to flush, read a header, or handle a pending
1890 * chunk.
1891 */
1892 if (pm->pending_chunk != 0)
1893 {
1894 png_save_uint_32(pm->buffer, pm->pending_len);
1895 png_save_uint_32(pm->buffer+4, pm->pending_chunk);
1896 pm->pending_len = 0;
1897 pm->pending_chunk = 0;
1898 }
1899 else
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001900 store_read_imp(&pm->this, pm->buffer, 8);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001901
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001902 pm->buffer_count = 8;
1903 pm->buffer_position = 0;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001904
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001905 /* Check for something to modify or a terminator chunk. */
1906 len = png_get_uint_32(pm->buffer);
1907 chunk = png_get_uint_32(pm->buffer+4);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001908
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001909 /* Terminators first, they may have to be delayed for added
1910 * chunks
1911 */
1912 if (chunk == CHUNK_PLTE || chunk == CHUNK_IDAT ||
1913 chunk == CHUNK_IEND)
1914 {
1915 mod = pm->modifications;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001916
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001917 while (mod != NULL)
1918 {
1919 if ((mod->add == chunk ||
1920 (mod->add == CHUNK_PLTE && chunk == CHUNK_IDAT)) &&
1921 mod->modify_fn != NULL && !mod->modified && !mod->added)
1922 {
1923 /* Regardless of what the modify function does do not run
1924 * this again.
1925 */
1926 mod->added = 1;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001927
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001928 if ((*mod->modify_fn)(pm, mod, 1 /*add*/))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001929 {
1930 /* Reset the CRC on a new chunk */
1931 if (pm->buffer_count > 0)
1932 modifier_setbuffer(pm);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001933
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001934 else
1935 {
1936 pm->buffer_position = 0;
1937 mod->removed = 1;
1938 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001939
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001940 /* The buffer has been filled with something (we assume)
1941 * so output this. Pend the current chunk.
1942 */
1943 pm->pending_len = len;
1944 pm->pending_chunk = chunk;
1945 break; /* out of while */
1946 }
1947 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001948
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001949 mod = mod->next;
1950 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001951
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001952 /* Don't do any further processing if the buffer was modified -
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001953 * otherwise the code will end up modifying a chunk that was
1954 * just added.
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001955 */
1956 if (mod != NULL)
1957 break; /* out of switch */
1958 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001959
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001960 /* If we get to here then this chunk may need to be modified. To
1961 * do this it must be less than 1024 bytes in total size, otherwise
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001962 * it just gets flushed.
1963 */
1964 if (len+12 <= sizeof pm->buffer)
1965 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001966 store_read_imp(&pm->this, pm->buffer+pm->buffer_count,
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001967 len+12-pm->buffer_count);
1968 pm->buffer_count = len+12;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001969
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001970 /* Check for a modification, else leave it be. */
1971 mod = pm->modifications;
1972 while (mod != NULL)
1973 {
1974 if (mod->chunk == chunk)
1975 {
1976 if (mod->modify_fn == NULL)
1977 {
1978 /* Remove this chunk */
1979 pm->buffer_count = pm->buffer_position = 0;
1980 mod->removed = 1;
1981 break; /* Terminate the while loop */
1982 }
1983
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001984 else if ((*mod->modify_fn)(pm, mod, 0))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001985 {
1986 mod->modified = 1;
1987 /* The chunk may have been removed: */
1988 if (pm->buffer_count == 0)
1989 {
1990 pm->buffer_position = 0;
1991 break;
1992 }
1993 modifier_setbuffer(pm);
1994 }
1995 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001996
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001997 mod = mod->next;
1998 }
1999 }
2000
2001 else
2002 pm->flush = len+12 - pm->buffer_count; /* data + crc */
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06002003
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002004 /* Take the data from the buffer (if there is any). */
2005 break;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002006 }
2007
2008 /* Here to read from the modifier buffer (not directly from
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05002009 * the store, as in the flush case above.)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002010 */
2011 cb = pm->buffer_count - pm->buffer_position;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002012
2013 if (cb > st)
2014 cb = st;
2015
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002016 memcpy(pb, pm->buffer + pm->buffer_position, cb);
2017 st -= cb;
2018 pb += cb;
2019 pm->buffer_position += cb;
2020 }
2021}
2022
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002023/* The callback: */
2024static void
2025modifier_read(png_structp pp, png_bytep pb, png_size_t st)
2026{
2027 png_modifier *pm = png_get_io_ptr(pp);
2028
2029 if (pm == NULL || pm->this.pread != pp)
2030 png_error(pp, "bad modifier_read call");
2031
2032 modifier_read_imp(pm, pb, st);
2033}
2034
2035/* Like store_progressive_read but the data is getting changed as we go so we
2036 * need a local buffer.
2037 */
2038static void
2039modifier_progressive_read(png_modifier *pm, png_structp pp, png_infop pi)
2040{
2041 if (pm->this.pread != pp || pm->this.current == NULL ||
2042 pm->this.next == NULL)
2043 png_error(pp, "store state damaged (progressive)");
2044
2045 /* This is another Horowitz and Hill random noise generator. In this case
2046 * the aim is to stress the progressive reader with truely horrible variable
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06002047 * buffer sizes in the range 1..500, so a sequence of 9 bit random numbers
2048 * is generated. We could probably just count from 1 to 32767 and get as
2049 * good a result.
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002050 */
2051 for (;;)
2052 {
2053 static png_uint_32 noise = 1;
2054 png_size_t cb, cbAvail;
2055 png_byte buffer[512];
2056
2057 /* Generate 15 more bits of stuff: */
2058 noise = (noise << 9) | ((noise ^ (noise >> (9-5))) & 0x1ff);
2059 cb = noise & 0x1ff;
2060
2061 /* Check that this number of bytes are available (in the current buffer.)
2062 * (This doesn't quite work - the modifier might delete a chunk; unlikely
2063 * but possible, it doesn't happen at present because the modifier only
2064 * adds chunks to standard images.)
2065 */
2066 cbAvail = store_read_buffer_avail(&pm->this);
2067 if (pm->buffer_count > pm->buffer_position)
2068 cbAvail += pm->buffer_count - pm->buffer_position;
2069
2070 if (cb > cbAvail)
2071 {
2072 /* Check for EOF: */
2073 if (cbAvail == 0)
2074 break;
2075
2076 cb = cbAvail;
2077 }
2078
2079 modifier_read_imp(pm, buffer, cb);
2080 png_process_data(pp, pi, buffer, cb);
2081 }
2082
2083 /* Check the invariants at the end (if this fails it's a problem in this
2084 * file!)
2085 */
2086 if (pm->buffer_count > pm->buffer_position ||
2087 pm->this.next != &pm->this.current->data ||
2088 pm->this.readpos < pm->this.current->datacount)
2089 png_error(pp, "progressive read implementation error");
2090}
2091
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002092/* Set up a modifier. */
2093static png_structp
2094set_modifier_for_read(png_modifier *pm, png_infopp ppi, png_uint_32 id,
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05002095 PNG_CONST char *name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002096{
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05002097 /* Do this first so that the modifier fields are cleared even if an error
2098 * happens allocating the png_struct. No allocation is done here so no
2099 * cleanup is required.
2100 */
2101 pm->state = modifier_start;
2102 pm->bit_depth = 0;
2103 pm->colour_type = 255;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002104
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05002105 pm->pending_len = 0;
2106 pm->pending_chunk = 0;
2107 pm->flush = 0;
2108 pm->buffer_count = 0;
2109 pm->buffer_position = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002110
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05002111 return set_store_for_read(&pm->this, ppi, id, name);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002112}
John Bowler4a12f4a2011-04-17 18:34:22 -05002113#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002114
2115/***************************** STANDARD PNG FILES *****************************/
2116/* Standard files - write and save standard files. */
John Bowler660c6e42010-12-19 06:22:23 -06002117/* There are two basic forms of standard images. Those which attempt to have
2118 * all the possible pixel values (not possible for 16bpp images, but a range of
2119 * values are produced) and those which have a range of image sizes. The former
2120 * are used for testing transforms, in particular gamma correction and bit
2121 * reduction and increase. The latter are reserved for testing the behavior of
2122 * libpng with respect to 'odd' image sizes - particularly small images where
2123 * rows become 1 byte and interlace passes disappear.
2124 *
2125 * The first, most useful, set are the 'transform' images, the second set of
2126 * small images are the 'size' images.
2127 *
2128 * The transform files are constructed with rows which fit into a 1024 byte row
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002129 * buffer. This makes allocation easier below. Further regardless of the file
John Bowler660c6e42010-12-19 06:22:23 -06002130 * format every row has 128 pixels (giving 1024 bytes for 64bpp formats).
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002131 *
2132 * Files are stored with no gAMA or sBIT chunks, with a PLTE only when needed
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002133 * and with an ID derived from the colour type, bit depth and interlace type
John Bowler660c6e42010-12-19 06:22:23 -06002134 * as above (FILEID). The width (128) and height (variable) are not stored in
2135 * the FILEID - instead the fields are set to 0, indicating a transform file.
2136 *
2137 * The size files ar constructed with rows a maximum of 128 bytes wide, allowing
2138 * a maximum width of 16 pixels (for the 64bpp case.) They also have a maximum
2139 * height of 16 rows. The width and height are stored in the FILEID and, being
2140 * non-zero, indicate a size file.
John Bowler9994f252011-05-15 18:52:39 -05002141 *
2142 * For palette image (colour type 3) multiple transform images are stored with
2143 * the same bit depth to allow testing of more colour combinations -
2144 * particularly important for testing the gamma code because libpng uses a
2145 * different code path for palette images. For size images a single palette is
2146 * used.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002147 */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002148
John Bowler9994f252011-05-15 18:52:39 -05002149/* Make a 'standard' palette. Because there are only 256 entries in a palette
2150 * (maximum) this actually makes a random palette in the hope that enough tests
2151 * will catch enough errors. (Note that the same palette isn't produced every
2152 * time for the same test - it depends on what previous tests have been run -
2153 * but a given set of arguments to pngvalid will always produce the same palette
2154 * at the same test! This is why pseudo-random number generators are useful for
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -05002155 * testing.)
John Bowler9994f252011-05-15 18:52:39 -05002156 *
2157 * The store must be open for write when this is called, otherwise an internal
2158 * error will occur. This routine contains its own magic number seed, so the
2159 * palettes generated don't change if there are intervening errors (changing the
2160 * calls to the store_mark seed.)
2161 */
2162static store_palette_entry *
2163make_standard_palette(png_store* ps, int npalette, int do_tRNS)
2164{
2165 static png_uint_32 palette_seed[2] = { 0x87654321, 9 };
2166
2167 int i = 0;
2168 png_byte values[256][4];
2169
2170 /* Always put in black and white plus the six primary and secondary colors.
2171 */
2172 for (; i<8; ++i)
2173 {
2174 values[i][1] = (i&1) ? 255 : 0;
2175 values[i][2] = (i&2) ? 255 : 0;
2176 values[i][3] = (i&4) ? 255 : 0;
2177 }
2178
2179 /* Then add 62 greys (one quarter of the remaining 256 slots). */
2180 {
2181 int j = 0;
2182 png_byte random_bytes[4];
2183 png_byte need[256];
2184
2185 need[0] = 0; /*got black*/
2186 memset(need+1, 1, (sizeof need)-2); /*need these*/
2187 need[255] = 0; /*but not white*/
2188
2189 while (i<70)
2190 {
2191 png_byte b;
2192
2193 if (j==0)
2194 {
2195 make_four_random_bytes(palette_seed, random_bytes);
2196 j = 4;
2197 }
2198
2199 b = random_bytes[--j];
2200 if (need[b])
2201 {
2202 values[i][1] = b;
2203 values[i][2] = b;
2204 values[i++][3] = b;
2205 }
2206 }
2207 }
2208
2209 /* Finally add 192 colors at random - don't worry about matches to things we
2210 * already have, chance is less than 1/65536. Don't worry about greys,
2211 * chance is the same, so we get a duplicate or extra gray less than 1 time
2212 * in 170.
2213 */
2214 for (; i<256; ++i)
2215 make_four_random_bytes(palette_seed, values[i]);
2216
2217 /* Fill in the alpha values in the first byte. Just use all possible values
2218 * (0..255) in an apparently random order:
2219 */
2220 {
2221 store_palette_entry *palette;
2222 png_byte selector[4];
2223
2224 make_four_random_bytes(palette_seed, selector);
2225
2226 if (do_tRNS)
2227 for (i=0; i<256; ++i)
2228 values[i][0] = (png_byte)(i ^ selector[0]);
2229
2230 else
2231 for (i=0; i<256; ++i)
2232 values[i][0] = 255; /* no transparency/tRNS chunk */
2233
2234 /* 'values' contains 256 ARGB values, but we only need 'npalette'.
2235 * 'npalette' will always be a power of 2: 2, 4, 16 or 256. In the low
2236 * bit depth cases select colors at random, else it is difficult to have
2237 * a set of low bit depth palette test with any chance of a reasonable
2238 * range of colors. Do this by randomly permuting values into the low
2239 * 'npalette' entries using an XOR mask generated here. This also
2240 * permutes the npalette == 256 case in a potentially useful way (there is
2241 * no relationship between palette index and the color value therein!)
2242 */
2243 palette = store_write_palette(ps, npalette);
2244
2245 for (i=0; i<npalette; ++i)
2246 {
2247 palette[i].alpha = values[i ^ selector[1]][0];
2248 palette[i].red = values[i ^ selector[1]][1];
2249 palette[i].green = values[i ^ selector[1]][2];
2250 palette[i].blue = values[i ^ selector[1]][3];
2251 }
2252
2253 return palette;
2254 }
2255}
2256
2257/* Initialize a standard palette on a write stream. The 'do_tRNS' argument
2258 * indicates whether or not to also set the tRNS chunk.
2259 */
2260static void
2261init_standard_palette(png_store *ps, png_structp pp, png_infop pi, int npalette,
2262 int do_tRNS)
2263{
2264 store_palette_entry *ppal = make_standard_palette(ps, npalette, do_tRNS);
2265
2266 {
2267 int i;
2268 png_color palette[256];
2269
2270 /* Set all entries to detect overread errors. */
2271 for (i=0; i<npalette; ++i)
2272 {
2273 palette[i].red = ppal[i].red;
2274 palette[i].green = ppal[i].green;
2275 palette[i].blue = ppal[i].blue;
2276 }
2277
2278 /* Just in case fill in the rest with detectable values: */
2279 for (; i<256; ++i)
2280 palette[i].red = palette[i].green = palette[i].blue = 42;
2281
2282 png_set_PLTE(pp, pi, palette, npalette);
2283 }
2284
2285 if (do_tRNS)
2286 {
2287 int i, j;
2288 png_byte tRNS[256];
2289
2290 /* Set all the entries, but skip trailing opaque entries */
2291 for (i=j=0; i<npalette; ++i)
2292 if ((tRNS[i] = ppal[i].alpha) < 255)
2293 j = i+1;
2294
2295 /* Fill in the remainder with a detectable value: */
2296 for (; i<256; ++i)
2297 tRNS[i] = 24;
2298
2299 if (j > 0)
2300 png_set_tRNS(pp, pi, tRNS, j, 0/*color*/);
2301 }
2302}
2303
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -05002304/* The number of passes is related to the interlace type. There was no libpng
John Bowler660c6e42010-12-19 06:22:23 -06002305 * API to determine this prior to 1.5, so we need an inquiry function:
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002306 */
2307static int
2308npasses_from_interlace_type(png_structp pp, int interlace_type)
2309{
2310 switch (interlace_type)
2311 {
2312 default:
2313 png_error(pp, "invalid interlace type");
2314
2315 case PNG_INTERLACE_NONE:
2316 return 1;
2317
2318 case PNG_INTERLACE_ADAM7:
John Bowler660c6e42010-12-19 06:22:23 -06002319 return PNG_INTERLACE_ADAM7_PASSES;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002320 }
2321}
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002322
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05002323static unsigned int
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002324bit_size(png_structp pp, png_byte colour_type, png_byte bit_depth)
2325{
2326 switch (colour_type)
2327 {
John Bowlerd2f0bc22011-06-11 06:42:06 -05002328 default: png_error(pp, "invalid color type");
2329
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002330 case 0: return bit_depth;
2331
2332 case 2: return 3*bit_depth;
2333
2334 case 3: return bit_depth;
2335
2336 case 4: return 2*bit_depth;
2337
2338 case 6: return 4*bit_depth;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002339 }
2340}
2341
John Bowler660c6e42010-12-19 06:22:23 -06002342#define TRANSFORM_WIDTH 128U
2343#define TRANSFORM_ROWMAX (TRANSFORM_WIDTH*8U)
2344#define SIZE_ROWMAX (16*8U) /* 16 pixels, max 8 bytes each - 128 bytes */
2345#define STANDARD_ROWMAX TRANSFORM_ROWMAX /* The larger of the two */
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05002346#define SIZE_HEIGHTMAX 16 /* Maximum range of size images */
John Bowler660c6e42010-12-19 06:22:23 -06002347
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002348static size_t
John Bowler660c6e42010-12-19 06:22:23 -06002349transform_rowsize(png_structp pp, png_byte colour_type, png_byte bit_depth)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002350{
John Bowler660c6e42010-12-19 06:22:23 -06002351 return (TRANSFORM_WIDTH * bit_size(pp, colour_type, bit_depth)) / 8;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002352}
2353
John Bowler660c6e42010-12-19 06:22:23 -06002354/* transform_width(pp, colour_type, bit_depth) current returns the same number
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05002355 * every time, so just use a macro:
2356 */
John Bowler660c6e42010-12-19 06:22:23 -06002357#define transform_width(pp, colour_type, bit_depth) TRANSFORM_WIDTH
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002358
2359static png_uint_32
John Bowler660c6e42010-12-19 06:22:23 -06002360transform_height(png_structp pp, png_byte colour_type, png_byte bit_depth)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002361{
2362 switch (bit_size(pp, colour_type, bit_depth))
2363 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002364 case 1:
2365 case 2:
2366 case 4:
2367 return 1; /* Total of 128 pixels */
2368
2369 case 8:
2370 return 2; /* Total of 256 pixels/bytes */
2371
2372 case 16:
2373 return 512; /* Total of 65536 pixels */
2374
2375 case 24:
2376 case 32:
2377 return 512; /* 65536 pixels */
2378
2379 case 48:
2380 case 64:
2381 return 2048;/* 4 x 65536 pixels. */
John Bowler9994f252011-05-15 18:52:39 -05002382# define TRANSFORM_HEIGHTMAX 2048
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002383
2384 default:
2385 return 0; /* Error, will be caught later */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002386 }
2387}
2388
John Bowler660c6e42010-12-19 06:22:23 -06002389/* The following can only be defined here, now we have the definitions
2390 * of the transform image sizes.
2391 */
2392static png_uint_32
2393standard_width(png_structp pp, png_uint_32 id)
2394{
2395 png_uint_32 width = WIDTH_FROM_ID(id);
John Bowlerafea7d12011-01-28 06:38:14 -06002396 UNUSED(pp)
John Bowler660c6e42010-12-19 06:22:23 -06002397
2398 if (width == 0)
2399 width = transform_width(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
2400
2401 return width;
2402}
2403
2404static png_uint_32
2405standard_height(png_structp pp, png_uint_32 id)
2406{
2407 png_uint_32 height = HEIGHT_FROM_ID(id);
2408
2409 if (height == 0)
2410 height = transform_height(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
2411
2412 return height;
2413}
2414
2415static png_uint_32
2416standard_rowsize(png_structp pp, png_uint_32 id)
2417{
2418 png_uint_32 width = standard_width(pp, id);
2419
2420 /* This won't overflow: */
2421 width *= bit_size(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
2422 return (width + 7) / 8;
2423}
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002424
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002425static void
John Bowler660c6e42010-12-19 06:22:23 -06002426transform_row(png_structp pp, png_byte buffer[TRANSFORM_ROWMAX],
2427 png_byte colour_type, png_byte bit_depth, png_uint_32 y)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002428{
2429 png_uint_32 v = y << 7;
2430 png_uint_32 i = 0;
2431
2432 switch (bit_size(pp, colour_type, bit_depth))
2433 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002434 case 1:
2435 while (i<128/8) buffer[i] = v & 0xff, v += 17, ++i;
2436 return;
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002437
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002438 case 2:
2439 while (i<128/4) buffer[i] = v & 0xff, v += 33, ++i;
2440 return;
2441
2442 case 4:
2443 while (i<128/2) buffer[i] = v & 0xff, v += 65, ++i;
2444 return;
2445
2446 case 8:
2447 /* 256 bytes total, 128 bytes in each row set as follows: */
2448 while (i<128) buffer[i] = v & 0xff, ++v, ++i;
2449 return;
2450
2451 case 16:
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002452 /* Generate all 65536 pixel values in order, which includes the 8 bit
2453 * GA case as well as the 16 bit G case.
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002454 */
2455 while (i<128)
2456 buffer[2*i] = (v>>8) & 0xff, buffer[2*i+1] = v & 0xff, ++v, ++i;
2457
2458 return;
2459
2460 case 24:
2461 /* 65535 pixels, but rotate the values. */
2462 while (i<128)
2463 {
2464 /* Three bytes per pixel, r, g, b, make b by r^g */
2465 buffer[3*i+0] = (v >> 8) & 0xff;
2466 buffer[3*i+1] = v & 0xff;
2467 buffer[3*i+2] = ((v >> 8) ^ v) & 0xff;
2468 ++v;
2469 ++i;
2470 }
2471
2472 return;
2473
2474 case 32:
2475 /* 65535 pixels, r, g, b, a; just replicate */
2476 while (i<128)
2477 {
2478 buffer[4*i+0] = (v >> 8) & 0xff;
2479 buffer[4*i+1] = v & 0xff;
2480 buffer[4*i+2] = (v >> 8) & 0xff;
2481 buffer[4*i+3] = v & 0xff;
2482 ++v;
2483 ++i;
2484 }
2485
2486 return;
2487
2488 case 48:
2489 /* y is maximum 2047, giving 4x65536 pixels, make 'r' increase by 1 at
2490 * each pixel, g increase by 257 (0x101) and 'b' by 0x1111:
2491 */
2492 while (i<128)
2493 {
2494 png_uint_32 t = v++;
2495 buffer[6*i+0] = (t >> 8) & 0xff;
2496 buffer[6*i+1] = t & 0xff;
2497 t *= 257;
2498 buffer[6*i+2] = (t >> 8) & 0xff;
2499 buffer[6*i+3] = t & 0xff;
2500 t *= 17;
2501 buffer[6*i+4] = (t >> 8) & 0xff;
2502 buffer[6*i+5] = t & 0xff;
2503 ++i;
2504 }
2505
2506 return;
2507
2508 case 64:
2509 /* As above in the 32 bit case. */
2510 while (i<128)
2511 {
2512 png_uint_32 t = v++;
2513 buffer[8*i+0] = (t >> 8) & 0xff;
2514 buffer[8*i+1] = t & 0xff;
2515 buffer[8*i+4] = (t >> 8) & 0xff;
2516 buffer[8*i+5] = t & 0xff;
2517 t *= 257;
2518 buffer[8*i+2] = (t >> 8) & 0xff;
2519 buffer[8*i+3] = t & 0xff;
2520 buffer[8*i+6] = (t >> 8) & 0xff;
2521 buffer[8*i+7] = t & 0xff;
2522 ++i;
2523 }
2524 return;
2525
2526 default:
2527 break;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002528 }
2529
2530 png_error(pp, "internal error");
2531}
2532
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002533/* This is just to do the right cast - could be changed to a function to check
2534 * 'bd' but there isn't much point.
2535 */
2536#define DEPTH(bd) ((png_byte)(1U << (bd)))
2537
John Bowler660c6e42010-12-19 06:22:23 -06002538/* Make a standardized image given a an image colour type, bit depth and
2539 * interlace type. The standard images have a very restricted range of
2540 * rows and heights and are used for testing transforms rather than image
2541 * layout details. See make_size_images below for a way to make images
2542 * that test odd sizes along with the libpng interlace handling.
2543 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002544static void
John Bowler660c6e42010-12-19 06:22:23 -06002545make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
John Bowler9994f252011-05-15 18:52:39 -05002546 png_byte PNG_CONST bit_depth, int palette_number, int interlace_type,
2547 png_const_charp name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002548{
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002549 context(ps, fault);
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05002550
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002551 Try
2552 {
2553 png_infop pi;
2554 png_structp pp = set_store_for_write(ps, &pi, name);
2555 png_uint_32 h;
2556
2557 /* In the event of a problem return control to the Catch statement below
2558 * to do the clean up - it is not possible to 'return' directly from a Try
2559 * block.
2560 */
2561 if (pp == NULL)
2562 Throw ps;
2563
John Bowler660c6e42010-12-19 06:22:23 -06002564 h = transform_height(pp, colour_type, bit_depth);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002565
John Bowler660c6e42010-12-19 06:22:23 -06002566 png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth), h,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002567 bit_depth, colour_type, interlace_type,
2568 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
2569
John Bowlerd273ad22011-05-07 21:00:28 -05002570#ifdef PNG_TEXT_SUPPORTED
2571 {
2572 static char key[] = "image name"; /* must be writeable */
2573 size_t pos;
2574 png_text text;
2575 char copy[FILE_NAME_SIZE];
2576
2577 /* Use a compressed text string to test the correct interaction of text
2578 * compression and IDAT compression.
2579 */
2580 text.compression = PNG_TEXT_COMPRESSION_zTXt;
2581 text.key = key;
2582 /* Yuck: the text must be writable! */
2583 pos = safecat(copy, sizeof copy, 0, ps->wname);
2584 text.text = copy;
2585 text.text_length = pos;
2586 text.itxt_length = 0;
2587 text.lang = 0;
2588 text.lang_key = 0;
2589
2590 png_set_text(pp, pi, &text, 1);
2591 }
2592#endif
2593
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002594 if (colour_type == 3) /* palette */
John Bowler9994f252011-05-15 18:52:39 -05002595 init_standard_palette(ps, pp, pi, 1U << bit_depth, 1/*do tRNS*/);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002596
2597 png_write_info(pp, pi);
2598
2599 if (png_get_rowbytes(pp, pi) !=
John Bowler660c6e42010-12-19 06:22:23 -06002600 transform_rowsize(pp, colour_type, bit_depth))
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002601 png_error(pp, "row size incorrect");
2602
2603 else
2604 {
2605 /* Somewhat confusingly this must be called *after* png_write_info
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002606 * because if it is called before, the information in *pp has not been
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002607 * updated to reflect the interlaced image.
2608 */
2609 int npasses = png_set_interlace_handling(pp);
2610 int pass;
2611
2612 if (npasses != npasses_from_interlace_type(pp, interlace_type))
2613 png_error(pp, "write: png_set_interlace_handling failed");
2614
John Bowler660c6e42010-12-19 06:22:23 -06002615 for (pass=0; pass<npasses; ++pass)
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002616 {
2617 png_uint_32 y;
2618
2619 for (y=0; y<h; ++y)
2620 {
John Bowler660c6e42010-12-19 06:22:23 -06002621 png_byte buffer[TRANSFORM_ROWMAX];
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002622
John Bowler660c6e42010-12-19 06:22:23 -06002623 transform_row(pp, buffer, colour_type, bit_depth, y);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002624 png_write_row(pp, buffer);
2625 }
2626 }
2627 }
2628
John Bowlerd273ad22011-05-07 21:00:28 -05002629#ifdef PNG_TEXT_SUPPORTED
2630 {
2631 static char key[] = "end marker";
2632 static char comment[] = "end";
2633 png_text text;
2634
2635 /* Use a compressed text string to test the correct interaction of text
2636 * compression and IDAT compression.
2637 */
2638 text.compression = PNG_TEXT_COMPRESSION_zTXt;
2639 text.key = key;
2640 text.text = comment;
2641 text.text_length = (sizeof comment)-1;
2642 text.itxt_length = 0;
2643 text.lang = 0;
2644 text.lang_key = 0;
2645
2646 png_set_text(pp, pi, &text, 1);
2647 }
2648#endif
2649
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002650 png_write_end(pp, pi);
2651
2652 /* And store this under the appropriate id, then clean up. */
John Bowler9994f252011-05-15 18:52:39 -05002653 store_storefile(ps, FILEID(colour_type, bit_depth, palette_number,
2654 interlace_type, 0, 0, 0));
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002655
2656 store_write_reset(ps);
2657 }
2658
2659 Catch(fault)
2660 {
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002661 /* Use the png_store returned by the exception. This may help the compiler
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05002662 * because 'ps' is not used in this branch of the setjmp. Note that fault
2663 * and ps will always be the same value.
2664 */
2665 store_write_reset(fault);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002666 }
2667}
2668
2669static void
John Bowler9994f252011-05-15 18:52:39 -05002670make_transform_images(png_store *ps)
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002671{
John Bowler9994f252011-05-15 18:52:39 -05002672 png_byte colour_type = 0;
2673 png_byte bit_depth = 0;
2674 int palette_number = 0;
2675
2676 /* This is in case of errors. */
2677 safecat(ps->test, sizeof ps->test, 0, "make standard images");
2678
2679 /* Use next_format to enumerate all the combinations we test, including
2680 * generating multiple low bit depth palette images.
2681 */
2682 while (next_format(&colour_type, &bit_depth, &palette_number))
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002683 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002684 int interlace_type;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002685
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002686 for (interlace_type = PNG_INTERLACE_NONE;
2687 interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002688 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002689 char name[FILE_NAME_SIZE];
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002690
John Bowler9994f252011-05-15 18:52:39 -05002691 standard_name(name, sizeof name, 0, colour_type, bit_depth,
2692 palette_number, interlace_type, 0, 0, 0);
2693 make_transform_image(ps, colour_type, bit_depth, palette_number,
2694 interlace_type, name);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002695 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002696 }
2697}
2698
John Bowler660c6e42010-12-19 06:22:23 -06002699/* The following two routines use the PNG interlace support macros from
2700 * png.h to interlace or deinterlace rows.
2701 */
2702static void
2703interlace_row(png_bytep buffer, png_const_bytep imageRow,
2704 unsigned int pixel_size, png_uint_32 w, int pass)
2705{
2706 png_uint_32 xin, xout, xstep;
2707
2708 /* Note that this can, trivially, be optimized to a memcpy on pass 7, the
2709 * code is presented this way to make it easier to understand. In practice
2710 * consult the code in the libpng source to see other ways of doing this.
2711 */
2712 xin = PNG_PASS_START_COL(pass);
2713 xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
2714
2715 for (xout=0; xin<w; xin+=xstep)
2716 {
2717 pixel_copy(buffer, xout, imageRow, xin, pixel_size);
2718 ++xout;
2719 }
2720}
2721
2722static void
2723deinterlace_row(png_bytep buffer, png_const_bytep row,
2724 unsigned int pixel_size, png_uint_32 w, int pass)
2725{
2726 /* The inverse of the above, 'row' is part of row 'y' of the output image,
2727 * in 'buffer'. The image is 'w' wide and this is pass 'pass', distribute
2728 * the pixels of row into buffer and return the number written (to allow
2729 * this to be checked).
2730 */
2731 png_uint_32 xin, xout, xstep;
2732
2733 xout = PNG_PASS_START_COL(pass);
2734 xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
2735
2736 for (xin=0; xout<w; xout+=xstep)
2737 {
2738 pixel_copy(buffer, xout, row, xin, pixel_size);
2739 ++xin;
2740 }
2741}
2742
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05002743/* Build a single row for the 'size' test images; this fills in only the
John Bowler660c6e42010-12-19 06:22:23 -06002744 * first bit_width bits of the sample row.
2745 */
2746static void
2747size_row(png_byte buffer[SIZE_ROWMAX], png_uint_32 bit_width, png_uint_32 y)
2748{
2749 /* height is in the range 1 to 16, so: */
2750 y = ((y & 1) << 7) + ((y & 2) << 6) + ((y & 4) << 5) + ((y & 8) << 4);
2751 /* the following ensures bits are set in small images: */
2752 y ^= 0xA5;
2753
2754 while (bit_width >= 8)
2755 *buffer++ = (png_byte)y++, bit_width -= 8;
2756
2757 /* There may be up to 7 remaining bits, these go in the most significant
2758 * bits of the byte.
2759 */
2760 if (bit_width > 0)
2761 {
2762 png_uint_32 mask = (1U<<(8-bit_width))-1;
2763 *buffer = (png_byte)((*buffer & mask) | (y & ~mask));
2764 }
2765}
2766
2767static void
2768make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
2769 png_byte PNG_CONST bit_depth, int PNG_CONST interlace_type,
2770 png_uint_32 PNG_CONST w, png_uint_32 PNG_CONST h,
2771 int PNG_CONST do_interlace)
2772{
John Bowler660c6e42010-12-19 06:22:23 -06002773 context(ps, fault);
2774
John Bowler660c6e42010-12-19 06:22:23 -06002775 Try
2776 {
2777 png_infop pi;
John Bowler56a739b2010-12-19 16:33:20 -06002778 png_structp pp;
John Bowler660c6e42010-12-19 06:22:23 -06002779 unsigned int pixel_size;
John Bowler56a739b2010-12-19 16:33:20 -06002780
2781 /* Make a name and get an appropriate id for the store: */
2782 char name[FILE_NAME_SIZE];
John Bowler9994f252011-05-15 18:52:39 -05002783 PNG_CONST png_uint_32 id = FILEID(colour_type, bit_depth, 0/*palette*/,
2784 interlace_type, w, h, do_interlace);
John Bowler56a739b2010-12-19 16:33:20 -06002785
2786 standard_name_from_id(name, sizeof name, 0, id);
2787 pp = set_store_for_write(ps, &pi, name);
John Bowler660c6e42010-12-19 06:22:23 -06002788
2789 /* In the event of a problem return control to the Catch statement below
2790 * to do the clean up - it is not possible to 'return' directly from a Try
2791 * block.
2792 */
2793 if (pp == NULL)
2794 Throw ps;
2795
2796 png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
2797 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
2798
John Bowler660c6e42010-12-19 06:22:23 -06002799 if (colour_type == 3) /* palette */
John Bowler9994f252011-05-15 18:52:39 -05002800 init_standard_palette(ps, pp, pi, 1U << bit_depth, 0/*do tRNS*/);
John Bowler660c6e42010-12-19 06:22:23 -06002801
2802 png_write_info(pp, pi);
2803
2804 /* Calculate the bit size, divide by 8 to get the byte size - this won't
2805 * overflow because we know the w values are all small enough even for
2806 * a system where 'unsigned int' is only 16 bits.
2807 */
2808 pixel_size = bit_size(pp, colour_type, bit_depth);
2809 if (png_get_rowbytes(pp, pi) != ((w * pixel_size) + 7) / 8)
2810 png_error(pp, "row size incorrect");
2811
2812 else
2813 {
2814 int npasses = npasses_from_interlace_type(pp, interlace_type);
2815 png_uint_32 y;
2816 int pass;
2817 png_byte image[16][SIZE_ROWMAX];
2818
2819 /* To help consistent error detection make the parts of this buffer
2820 * that aren't set below all '1':
2821 */
2822 memset(image, 0xff, sizeof image);
2823
2824 if (!do_interlace && npasses != png_set_interlace_handling(pp))
2825 png_error(pp, "write: png_set_interlace_handling failed");
2826
2827 /* Prepare the whole image first to avoid making it 7 times: */
2828 for (y=0; y<h; ++y)
2829 size_row(image[y], w * pixel_size, y);
2830
2831 for (pass=0; pass<npasses; ++pass)
2832 {
2833 /* The following two are for checking the macros: */
2834 PNG_CONST png_uint_32 wPass = PNG_PASS_COLS(w, pass);
2835
2836 /* If do_interlace is set we don't call png_write_row for every
2837 * row because some of them are empty. In fact, for a 1x1 image,
2838 * most of them are empty!
2839 */
2840 for (y=0; y<h; ++y)
2841 {
2842 png_const_bytep row = image[y];
2843 png_byte tempRow[SIZE_ROWMAX];
2844
2845 /* If do_interlace *and* the image is interlaced we
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05002846 * need a reduced interlace row; this may be reduced
John Bowler660c6e42010-12-19 06:22:23 -06002847 * to empty.
2848 */
2849 if (do_interlace && interlace_type == PNG_INTERLACE_ADAM7)
2850 {
2851 /* The row must not be written if it doesn't exist, notice
2852 * that there are two conditions here, either the row isn't
2853 * ever in the pass or the row would be but isn't wide
2854 * enough to contribute any pixels. In fact the wPass test
2855 * can be used to skip the whole y loop in this case.
2856 */
2857 if (PNG_ROW_IN_INTERLACE_PASS(y, pass) && wPass > 0)
2858 {
2859 /* Set to all 1's for error detection (libpng tends to
2860 * set unset things to 0).
2861 */
2862 memset(tempRow, 0xff, sizeof tempRow);
2863 interlace_row(tempRow, row, pixel_size, w, pass);
2864 row = tempRow;
2865 }
2866 else
2867 continue;
2868 }
2869
2870 /* Only get to here if the row has some pixels in it. */
2871 png_write_row(pp, row);
2872 }
2873 }
2874 }
2875
2876 png_write_end(pp, pi);
2877
2878 /* And store this under the appropriate id, then clean up. */
2879 store_storefile(ps, id);
2880
2881 store_write_reset(ps);
2882 }
2883
2884 Catch(fault)
2885 {
2886 /* Use the png_store returned by the exception. This may help the compiler
2887 * because 'ps' is not used in this branch of the setjmp. Note that fault
2888 * and ps will always be the same value.
2889 */
2890 store_write_reset(fault);
2891 }
2892}
2893
2894static void
2895make_size(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo,
2896 int PNG_CONST bdhi)
2897{
2898 for (; bdlo <= bdhi; ++bdlo)
2899 {
2900 png_uint_32 width;
2901
2902 for (width = 1; width <= 16; ++width)
2903 {
2904 png_uint_32 height;
2905
2906 for (height = 1; height <= 16; ++height)
2907 {
2908 /* The four combinations of DIY interlace and interlace or not -
2909 * no interlace + DIY should be identical to no interlace with
2910 * libpng doing it.
2911 */
2912 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
2913 width, height, 0);
2914 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
2915 width, height, 1);
2916 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
2917 width, height, 0);
2918 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
2919 width, height, 1);
2920 }
2921 }
2922 }
2923}
2924
2925static void
2926make_size_images(png_store *ps)
2927{
2928 /* This is in case of errors. */
2929 safecat(ps->test, sizeof ps->test, 0, "make size images");
2930
2931 /* Arguments are colour_type, low bit depth, high bit depth
2932 */
2933 make_size(ps, 0, 0, WRITE_BDHI);
2934 make_size(ps, 2, 3, WRITE_BDHI);
2935 make_size(ps, 3, 0, 3 /*palette: max 8 bits*/);
2936 make_size(ps, 4, 3, WRITE_BDHI);
2937 make_size(ps, 6, 3, WRITE_BDHI);
2938}
2939
2940/* Return a row based on image id and 'y' for checking: */
2941static void
2942standard_row(png_structp pp, png_byte std[STANDARD_ROWMAX], png_uint_32 id,
2943 png_uint_32 y)
2944{
2945 if (WIDTH_FROM_ID(id) == 0)
2946 transform_row(pp, std, COL_FROM_ID(id), DEPTH_FROM_ID(id), y);
2947 else
2948 size_row(std, WIDTH_FROM_ID(id) * bit_size(pp, COL_FROM_ID(id),
2949 DEPTH_FROM_ID(id)), y);
2950}
2951
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002952/* Tests - individual test cases */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002953/* Like 'make_standard' but errors are deliberately introduced into the calls
2954 * to ensure that they get detected - it should not be possible to write an
2955 * invalid image with libpng!
2956 */
John Bowler88b77cc2011-05-05 06:49:55 -05002957#ifdef PNG_WARNINGS_SUPPORTED
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002958static void
2959sBIT0_error_fn(png_structp pp, png_infop pi)
2960{
2961 /* 0 is invalid... */
2962 png_color_8 bad;
2963 bad.red = bad.green = bad.blue = bad.gray = bad.alpha = 0;
2964 png_set_sBIT(pp, pi, &bad);
2965}
2966
2967static void
2968sBIT_error_fn(png_structp pp, png_infop pi)
2969{
2970 png_byte bit_depth;
2971 png_color_8 bad;
2972
2973 if (png_get_color_type(pp, pi) == PNG_COLOR_TYPE_PALETTE)
2974 bit_depth = 8;
2975
2976 else
2977 bit_depth = png_get_bit_depth(pp, pi);
2978
2979 /* Now we know the bit depth we can easily generate an invalid sBIT entry */
2980 bad.red = bad.green = bad.blue = bad.gray = bad.alpha =
2981 (png_byte)(bit_depth+1);
2982 png_set_sBIT(pp, pi, &bad);
2983}
2984
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002985static PNG_CONST struct
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002986{
2987 void (*fn)(png_structp, png_infop);
2988 PNG_CONST char *msg;
2989 unsigned int warning :1; /* the error is a warning... */
2990} error_test[] =
2991 {
John Bowler88b77cc2011-05-05 06:49:55 -05002992 /* no warnings makes these errors undetectable. */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002993 { sBIT0_error_fn, "sBIT(0): failed to detect error", 1 },
2994 { sBIT_error_fn, "sBIT(too big): failed to detect error", 1 },
2995 };
2996
2997static void
John Bowler56a739b2010-12-19 16:33:20 -06002998make_error(png_store* volatile ps, png_byte PNG_CONST colour_type,
2999 png_byte bit_depth, int interlace_type, int test, png_const_charp name)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003000{
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003001 context(ps, fault);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003002
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003003 Try
3004 {
3005 png_structp pp;
3006 png_infop pi;
3007
3008 pp = set_store_for_write(ps, &pi, name);
3009
3010 if (pp == NULL)
3011 Throw ps;
3012
John Bowler660c6e42010-12-19 06:22:23 -06003013 png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth),
3014 transform_height(pp, colour_type, bit_depth), bit_depth, colour_type,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003015 interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
3016
3017 if (colour_type == 3) /* palette */
John Bowler9994f252011-05-15 18:52:39 -05003018 init_standard_palette(ps, pp, pi, 1U << bit_depth, 0/*do tRNS*/);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003019
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05003020 /* Time for a few errors; these are in various optional chunks, the
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003021 * standard tests test the standard chunks pretty well.
3022 */
John Bowler168a4332011-01-16 19:32:22 -06003023# define exception__prev exception_prev_1
3024# define exception__env exception_env_1
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003025 Try
3026 {
3027 /* Expect this to throw: */
3028 ps->expect_error = !error_test[test].warning;
3029 ps->expect_warning = error_test[test].warning;
3030 ps->saw_warning = 0;
3031 error_test[test].fn(pp, pi);
3032
3033 /* Normally the error is only detected here: */
3034 png_write_info(pp, pi);
3035
3036 /* And handle the case where it was only a warning: */
3037 if (ps->expect_warning && ps->saw_warning)
3038 Throw ps;
3039
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003040 /* If we get here there is a problem, we have success - no error or
3041 * no warning - when we shouldn't have success. Log an error.
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003042 */
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06003043 store_log(ps, pp, error_test[test].msg, 1 /*error*/);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003044 }
3045
3046 Catch (fault)
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003047 ps = fault; /* expected exit, make sure ps is not clobbered */
John Bowler168a4332011-01-16 19:32:22 -06003048#undef exception__prev
3049#undef exception__env
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003050
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003051 /* And clear these flags */
3052 ps->expect_error = 0;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003053 ps->expect_warning = 0;
3054
3055 /* Now write the whole image, just to make sure that the detected, or
3056 * undetected, errro has not created problems inside libpng.
3057 */
3058 if (png_get_rowbytes(pp, pi) !=
John Bowler660c6e42010-12-19 06:22:23 -06003059 transform_rowsize(pp, colour_type, bit_depth))
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003060 png_error(pp, "row size incorrect");
3061
3062 else
3063 {
John Bowler660c6e42010-12-19 06:22:23 -06003064 png_uint_32 h = transform_height(pp, colour_type, bit_depth);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003065 int npasses = png_set_interlace_handling(pp);
3066 int pass;
3067
3068 if (npasses != npasses_from_interlace_type(pp, interlace_type))
3069 png_error(pp, "write: png_set_interlace_handling failed");
3070
John Bowler660c6e42010-12-19 06:22:23 -06003071 for (pass=0; pass<npasses; ++pass)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003072 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003073 png_uint_32 y;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06003074
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003075 for (y=0; y<h; ++y)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003076 {
John Bowler660c6e42010-12-19 06:22:23 -06003077 png_byte buffer[TRANSFORM_ROWMAX];
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003078
John Bowler660c6e42010-12-19 06:22:23 -06003079 transform_row(pp, buffer, colour_type, bit_depth, y);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003080 png_write_row(pp, buffer);
3081 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003082 }
3083 }
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003084
3085 png_write_end(pp, pi);
3086
3087 /* The following deletes the file that was just written. */
3088 store_write_reset(ps);
3089 }
3090
3091 Catch(fault)
3092 {
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003093 store_write_reset(fault);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003094 }
3095}
3096
3097static int
3098make_errors(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
3099 int bdlo, int PNG_CONST bdhi)
3100{
3101 for (; bdlo <= bdhi; ++bdlo)
3102 {
3103 int interlace_type;
3104
3105 for (interlace_type = PNG_INTERLACE_NONE;
3106 interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
3107 {
3108 unsigned int test;
3109 char name[FILE_NAME_SIZE];
3110
John Bowler9994f252011-05-15 18:52:39 -05003111 standard_name(name, sizeof name, 0, colour_type, 1<<bdlo, 0,
3112 interlace_type, 0, 0, 0);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003113
3114 for (test=0; test<(sizeof error_test)/(sizeof error_test[0]); ++test)
3115 {
3116 make_error(&pm->this, colour_type, DEPTH(bdlo), interlace_type,
3117 test, name);
3118
3119 if (fail(pm))
3120 return 0;
3121 }
3122 }
3123 }
3124
3125 return 1; /* keep going */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003126}
John Bowler88b77cc2011-05-05 06:49:55 -05003127#endif
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003128
3129static void
3130perform_error_test(png_modifier *pm)
3131{
John Bowler88b77cc2011-05-05 06:49:55 -05003132#ifdef PNG_WARNINGS_SUPPORTED /* else there are no cases that work! */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003133 /* Need to do this here because we just write in this test. */
3134 safecat(pm->this.test, sizeof pm->this.test, 0, "error test");
3135
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003136 if (!make_errors(pm, 0, 0, WRITE_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003137 return;
3138
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003139 if (!make_errors(pm, 2, 3, WRITE_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003140 return;
3141
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003142 if (!make_errors(pm, 3, 0, 3))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003143 return;
3144
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003145 if (!make_errors(pm, 4, 3, WRITE_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003146 return;
3147
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003148 if (!make_errors(pm, 6, 3, WRITE_BDHI))
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003149 return;
John Bowler88b77cc2011-05-05 06:49:55 -05003150#else
3151 UNUSED(pm)
3152#endif
3153}
3154
3155/* This is just to validate the internal PNG formatting code - if this fails
3156 * then the warning messages the library outputs will probably be garbage.
3157 */
3158static void
3159perform_formatting_test(png_store *volatile ps)
3160{
3161#ifdef PNG_TIME_RFC1123_SUPPORTED
3162 /* The handle into the formatting code is the RFC1123 support; this test does
3163 * nothing if that is compiled out.
3164 */
3165 context(ps, fault);
3166
3167 Try
3168 {
3169 png_const_charp correct = "29 Aug 2079 13:53:60 +0000";
3170 png_const_charp result;
3171 png_structp pp;
3172 png_time pt;
3173
3174 pp = set_store_for_write(ps, NULL, "libpng formatting test");
3175
3176 if (pp == NULL)
3177 Throw ps;
3178
3179
3180 /* Arbitrary settings: */
3181 pt.year = 2079;
3182 pt.month = 8;
3183 pt.day = 29;
3184 pt.hour = 13;
3185 pt.minute = 53;
3186 pt.second = 60; /* a leap second */
3187
3188 result = png_convert_to_rfc1123(pp, &pt);
3189
3190 if (result == NULL)
3191 png_error(pp, "png_convert_to_rfc1123 failed");
3192
3193 if (strcmp(result, correct) != 0)
3194 {
3195 size_t pos = 0;
3196 char msg[128];
3197
3198 pos = safecat(msg, sizeof msg, pos, "png_convert_to_rfc1123(");
3199 pos = safecat(msg, sizeof msg, pos, correct);
3200 pos = safecat(msg, sizeof msg, pos, ") returned: '");
3201 pos = safecat(msg, sizeof msg, pos, result);
3202 pos = safecat(msg, sizeof msg, pos, "'");
3203
3204 png_error(pp, msg);
3205 }
3206
3207 store_write_reset(ps);
3208 }
3209
3210 Catch(fault)
3211 {
3212 store_write_reset(fault);
3213 }
3214#else
3215 UNUSED(ps)
3216#endif
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003217}
3218
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003219/* Because we want to use the same code in both the progressive reader and the
3220 * sequential reader it is necessary to deal with the fact that the progressive
3221 * reader callbacks only have one parameter (png_get_progressive_ptr()), so this
3222 * must contain all the test parameters and all the local variables directly
3223 * accessible to the sequential reader implementation.
3224 *
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003225 * The technique adopted is to reinvent part of what Dijkstra termed a
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003226 * 'display'; an array of pointers to the stack frames of enclosing functions so
3227 * that a nested function definition can access the local (C auto) variables of
3228 * the functions that contain its definition. In fact C provides the first
3229 * pointer (the local variables - the stack frame pointer) and the last (the
3230 * global variables - the BCPL global vector typically implemented as global
3231 * addresses), this code requires one more pointer to make the display - the
3232 * local variables (and function call parameters) of the function that actually
3233 * invokes either the progressive or sequential reader.
3234 *
3235 * Perhaps confusingly this technique is confounded with classes - the
3236 * 'standard_display' defined here is sub-classed as the 'gamma_display' below.
3237 * A gamma_display is a standard_display, taking advantage of the ANSI-C
3238 * requirement that the pointer to the first member of a structure must be the
3239 * same as the pointer to the structure. This allows us to reuse standard_
3240 * functions in the gamma test code; something that could not be done with
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05003241 * nested functions!
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003242 */
3243typedef struct standard_display
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003244{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003245 png_store* ps; /* Test parameters (passed to the function) */
3246 png_byte colour_type;
3247 png_byte bit_depth;
John Bowlerf21a0d02011-01-23 23:55:19 -06003248 png_byte red_sBIT; /* Input data sBIT values. */
3249 png_byte green_sBIT;
3250 png_byte blue_sBIT;
3251 png_byte alpha_sBIT;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003252 int interlace_type;
3253 png_uint_32 id; /* Calculated file ID */
3254 png_uint_32 w; /* Width of image */
3255 png_uint_32 h; /* Height of image */
3256 int npasses; /* Number of interlaced passes */
John Bowler660c6e42010-12-19 06:22:23 -06003257 png_uint_32 pixel_size; /* Width of one pixel in bits */
3258 png_uint_32 bit_width; /* Width of output row in bits */
3259 size_t cbRow; /* Bytes in a row of the output image */
3260 int do_interlace; /* Do interlacing internally */
John Bowler9994f252011-05-15 18:52:39 -05003261 int is_transparent; /* Transparency information was present. */
3262 int speed; /* Doing a speed test */
John Bowlerf21a0d02011-01-23 23:55:19 -06003263 struct
3264 {
3265 png_uint_16 red;
3266 png_uint_16 green;
3267 png_uint_16 blue;
3268 } transparent; /* The transparent color, if set. */
John Bowler9994f252011-05-15 18:52:39 -05003269 int npalette; /* Number of entries in the palette. */
3270 store_palette
John Bowlerafea7d12011-01-28 06:38:14 -06003271 palette;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003272} standard_display;
3273
3274static void
John Bowler660c6e42010-12-19 06:22:23 -06003275standard_display_init(standard_display *dp, png_store* ps, png_uint_32 id,
3276 int do_interlace)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003277{
3278 dp->ps = ps;
John Bowler660c6e42010-12-19 06:22:23 -06003279 dp->colour_type = COL_FROM_ID(id);
3280 dp->bit_depth = DEPTH_FROM_ID(id);
John Bowler9994f252011-05-15 18:52:39 -05003281 if (dp->colour_type == 3)
3282 dp->red_sBIT = dp->blue_sBIT = dp->green_sBIT = dp->alpha_sBIT = 8;
3283 else
3284 dp->red_sBIT = dp->blue_sBIT = dp->green_sBIT = dp->alpha_sBIT =
3285 dp->bit_depth;
John Bowler660c6e42010-12-19 06:22:23 -06003286 dp->interlace_type = INTERLACE_FROM_ID(id);
3287 dp->id = id;
3288 /* All the rest are filled in after the read_info: */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003289 dp->w = 0;
3290 dp->h = 0;
3291 dp->npasses = 0;
John Bowler660c6e42010-12-19 06:22:23 -06003292 dp->pixel_size = 0;
3293 dp->bit_width = 0;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003294 dp->cbRow = 0;
John Bowler660c6e42010-12-19 06:22:23 -06003295 dp->do_interlace = do_interlace;
John Bowlerf21a0d02011-01-23 23:55:19 -06003296 dp->is_transparent = 0;
John Bowler9994f252011-05-15 18:52:39 -05003297 dp->speed = ps->speed;
3298 dp->npalette = 0;
John Bowlerf21a0d02011-01-23 23:55:19 -06003299 /* Preset the transparent color to black: */
3300 memset(&dp->transparent, 0, sizeof dp->transparent);
3301 /* Preset the palette to full intensity/opaque througout: */
3302 memset(dp->palette, 0xff, sizeof dp->palette);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003303}
3304
John Bowler9994f252011-05-15 18:52:39 -05003305/* Initialize the palette fields - this must be done later because the palette
3306 * comes from the particular png_store_file that is selected.
3307 */
3308static void
3309standard_palette_init(standard_display *dp)
3310{
3311 store_palette_entry *palette = store_current_palette(dp->ps, &dp->npalette);
3312
3313 /* The remaining entries remain white/opaque. */
3314 if (dp->npalette > 0)
3315 {
3316 int i = dp->npalette;
3317 memcpy(dp->palette, palette, i * sizeof *palette);
3318
3319 /* Check for a non-opaque palette entry: */
3320 while (--i >= 0)
3321 if (palette[i].alpha < 255)
3322 break;
3323
3324# ifdef __GNUC__
3325 /* GCC can't handle the more obviously optimizable version. */
3326 if (i >= 0)
3327 dp->is_transparent = 1;
3328 else
3329 dp->is_transparent = 0;
3330# else
3331 dp->is_transparent = (i >= 0);
3332# endif
3333 }
3334}
3335
3336/* Utility to read the palette from the PNG file and convert it into
3337 * store_palette format. This returns 1 if there is any transparency in the
3338 * palette (it does not check for a transparent colour in the non-palette case.)
John Bowlerafea7d12011-01-28 06:38:14 -06003339 */
3340static int
John Bowler9994f252011-05-15 18:52:39 -05003341read_palette(store_palette palette, int *npalette, png_structp pp, png_infop pi)
John Bowlerafea7d12011-01-28 06:38:14 -06003342{
3343 png_colorp pal;
3344 png_bytep trans_alpha;
3345 int num;
3346
3347 pal = 0;
John Bowler9994f252011-05-15 18:52:39 -05003348 *npalette = -1;
John Bowlerafea7d12011-01-28 06:38:14 -06003349
John Bowler9994f252011-05-15 18:52:39 -05003350 if (png_get_PLTE(pp, pi, &pal, npalette) & PNG_INFO_PLTE)
3351 {
3352 int i = *npalette;
3353
3354 if (i <= 0 || i > 256)
3355 png_error(pp, "validate: invalid PLTE count");
3356
3357 while (--i >= 0)
John Bowlerafea7d12011-01-28 06:38:14 -06003358 {
3359 palette[i].red = pal[i].red;
3360 palette[i].green = pal[i].green;
3361 palette[i].blue = pal[i].blue;
3362 }
3363
John Bowler9994f252011-05-15 18:52:39 -05003364 /* Mark the remainder of the entries with a flag value (other than
3365 * white/opaque which is the flag value stored above.)
3366 */
3367 memset(palette + *npalette, 126, (256-*npalette) * sizeof *palette);
John Bowlerafea7d12011-01-28 06:38:14 -06003368 }
3369
3370 else /* !png_get_PLTE */
John Bowler9994f252011-05-15 18:52:39 -05003371 {
3372 if (*npalette != (-1))
3373 png_error(pp, "validate: invalid PLTE result");
3374 /* But there is no palette, so record this: */
3375 *npalette = 0;
3376 memset(palette, 113, sizeof palette);
3377 }
John Bowlerafea7d12011-01-28 06:38:14 -06003378
3379 trans_alpha = 0;
John Bowler9994f252011-05-15 18:52:39 -05003380 num = 2; /* force error below */
3381 if ((png_get_tRNS(pp, pi, &trans_alpha, &num, 0) & PNG_INFO_tRNS) != 0 &&
3382 (trans_alpha != NULL || num != 1/*returns 1 for a transparent color*/) &&
3383 /* Oops, if a palette tRNS gets expanded png_read_update_info (at least so
3384 * far as 1.5.3) does not zap the trans_alpha pointer, only num_trans, so
3385 * in the above call we get a success, we get a pointer (who knows what
3386 * to) and we get num_trans == 0:
3387 */
3388 !(trans_alpha != NULL && num == 0)) /* TODO: fix this in libpng. */
John Bowlerafea7d12011-01-28 06:38:14 -06003389 {
3390 int i;
3391
3392 /* Any of these are crash-worthy - given the implementation of
3393 * png_get_tRNS up to 1.5 an app won't crash if it just checks the
3394 * result above and fails to check that the variables it passed have
3395 * actually been filled in! Note that if the app were to pass the
3396 * last, png_color_16p, variable too it couldn't rely on this.
3397 */
John Bowler9994f252011-05-15 18:52:39 -05003398 if (trans_alpha == NULL || num <= 0 || num > 256 || num > *npalette)
John Bowlerafea7d12011-01-28 06:38:14 -06003399 png_error(pp, "validate: unexpected png_get_tRNS (palette) result");
3400
3401 for (i=0; i<num; ++i)
3402 palette[i].alpha = trans_alpha[i];
3403
John Bowler9994f252011-05-15 18:52:39 -05003404 for (num=*npalette; i<num; ++i)
John Bowlerafea7d12011-01-28 06:38:14 -06003405 palette[i].alpha = 255;
3406
John Bowler9994f252011-05-15 18:52:39 -05003407 for (; i<256; ++i)
3408 palette[i].alpha = 33; /* flag value */
3409
John Bowlerafea7d12011-01-28 06:38:14 -06003410 return 1; /* transparency */
3411 }
3412
3413 else
3414 {
John Bowler9994f252011-05-15 18:52:39 -05003415 /* No palette transparency - just set the alpha channel to opaque. */
John Bowlerafea7d12011-01-28 06:38:14 -06003416 int i;
3417
John Bowler9994f252011-05-15 18:52:39 -05003418 for (i=0, num=*npalette; i<num; ++i)
John Bowlerafea7d12011-01-28 06:38:14 -06003419 palette[i].alpha = 255;
3420
John Bowler9994f252011-05-15 18:52:39 -05003421 for (; i<256; ++i)
3422 palette[i].alpha = 55; /* flag value */
3423
John Bowlerafea7d12011-01-28 06:38:14 -06003424 return 0; /* no transparency */
3425 }
3426}
3427
John Bowler9994f252011-05-15 18:52:39 -05003428/* Utility to validate the palette if it should not have changed (the
3429 * non-transform case).
3430 */
3431static void
3432standard_palette_validate(standard_display *dp, png_structp pp, png_infop pi)
3433{
3434 int npalette;
3435 store_palette palette;
3436
3437 if (read_palette(palette, &npalette, pp, pi) != dp->is_transparent)
3438 png_error(pp, "validate: palette transparency changed");
3439
3440 if (npalette != dp->npalette)
3441 {
3442 size_t pos = 0;
3443 char msg[64];
3444
3445 pos = safecat(msg, sizeof msg, pos, "validate: palette size changed: ");
3446 pos = safecatn(msg, sizeof msg, pos, dp->npalette);
3447 pos = safecat(msg, sizeof msg, pos, " -> ");
3448 pos = safecatn(msg, sizeof msg, pos, npalette);
3449 png_error(pp, msg);
3450 }
3451
3452 {
3453 int i = npalette; /* npalette is aliased */
3454
3455 while (--i >= 0)
3456 if (palette[i].red != dp->palette[i].red ||
3457 palette[i].green != dp->palette[i].green ||
3458 palette[i].blue != dp->palette[i].blue ||
3459 palette[i].alpha != dp->palette[i].alpha)
3460 png_error(pp, "validate: PLTE or tRNS chunk changed");
3461 }
3462}
3463
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003464/* By passing a 'standard_display' the progressive callbacks can be used
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003465 * directly by the sequential code, the functions suffixed "_imp" are the
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003466 * implementations, the functions without the suffix are the callbacks.
3467 *
3468 * The code for the info callback is split into two because this callback calls
3469 * png_read_update_info or png_start_read_image and what gets called depends on
3470 * whether the info needs updating (we want to test both calls in pngvalid.)
3471 */
3472static void
3473standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
3474{
3475 if (png_get_bit_depth(pp, pi) != dp->bit_depth)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003476 png_error(pp, "validate: bit depth changed");
3477
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003478 if (png_get_color_type(pp, pi) != dp->colour_type)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003479 png_error(pp, "validate: color type changed");
3480
3481 if (png_get_filter_type(pp, pi) != PNG_FILTER_TYPE_BASE)
3482 png_error(pp, "validate: filter type changed");
3483
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003484 if (png_get_interlace_type(pp, pi) != dp->interlace_type)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003485 png_error(pp, "validate: interlacing changed");
3486
3487 if (png_get_compression_type(pp, pi) != PNG_COMPRESSION_TYPE_BASE)
3488 png_error(pp, "validate: compression type changed");
3489
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003490 dp->w = png_get_image_width(pp, pi);
3491
John Bowler660c6e42010-12-19 06:22:23 -06003492 if (dp->w != standard_width(pp, dp->id))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003493 png_error(pp, "validate: image width changed");
3494
3495 dp->h = png_get_image_height(pp, pi);
3496
John Bowler660c6e42010-12-19 06:22:23 -06003497 if (dp->h != standard_height(pp, dp->id))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003498 png_error(pp, "validate: image height changed");
3499
John Bowlerf21a0d02011-01-23 23:55:19 -06003500 /* Record (but don't check at present) the input sBIT according to the colour
3501 * type information.
3502 */
3503 {
3504 png_color_8p sBIT = 0;
3505
3506 if (png_get_sBIT(pp, pi, &sBIT) & PNG_INFO_sBIT)
3507 {
3508 int sBIT_invalid = 0;
3509
3510 if (sBIT == 0)
3511 png_error(pp, "validate: unexpected png_get_sBIT result");
3512
3513 if (dp->colour_type & PNG_COLOR_MASK_COLOR)
3514 {
3515 if (sBIT->red == 0 || sBIT->red > dp->bit_depth)
3516 sBIT_invalid = 1;
3517 else
3518 dp->red_sBIT = sBIT->red;
3519
3520 if (sBIT->green == 0 || sBIT->green > dp->bit_depth)
3521 sBIT_invalid = 1;
3522 else
3523 dp->green_sBIT = sBIT->green;
3524
3525 if (sBIT->blue == 0 || sBIT->blue > dp->bit_depth)
3526 sBIT_invalid = 1;
3527 else
3528 dp->blue_sBIT = sBIT->blue;
3529 }
3530
3531 else /* !COLOR */
3532 {
3533 if (sBIT->gray == 0 || sBIT->gray > dp->bit_depth)
3534 sBIT_invalid = 1;
3535 else
3536 dp->blue_sBIT = dp->green_sBIT = dp->red_sBIT = sBIT->gray;
3537 }
3538
3539 /* All 8 bits in tRNS for a palette image are significant - see the
3540 * spec.
3541 */
3542 if (dp->colour_type & PNG_COLOR_MASK_ALPHA)
3543 {
3544 if (sBIT->alpha == 0 || sBIT->alpha > dp->bit_depth)
3545 sBIT_invalid = 1;
3546 else
3547 dp->alpha_sBIT = sBIT->alpha;
3548 }
3549
3550 if (sBIT_invalid)
3551 png_error(pp, "validate: sBIT value out of range");
3552 }
3553 }
3554
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003555 /* Important: this is validating the value *before* any transforms have been
3556 * put in place. It doesn't matter for the standard tests, where there are
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003557 * no transforms, but it does for other tests where rowbytes may change after
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003558 * png_read_update_info.
3559 */
John Bowler660c6e42010-12-19 06:22:23 -06003560 if (png_get_rowbytes(pp, pi) != standard_rowsize(pp, dp->id))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003561 png_error(pp, "validate: row size changed");
3562
John Bowler9994f252011-05-15 18:52:39 -05003563 /* Validate the colour type 3 palette (this can be present on other color
3564 * types.)
John Bowlerf21a0d02011-01-23 23:55:19 -06003565 */
John Bowler9994f252011-05-15 18:52:39 -05003566 standard_palette_validate(dp, pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003567
John Bowler9994f252011-05-15 18:52:39 -05003568 /* In any case always check for a tranparent color (notice that the
3569 * colour type 3 case must not give a successful return on the get_tRNS call
3570 * with these arguments!)
3571 */
John Bowlerf21a0d02011-01-23 23:55:19 -06003572 {
3573 png_color_16p trans_color = 0;
3574
3575 if (png_get_tRNS(pp, pi, 0, 0, &trans_color) & PNG_INFO_tRNS)
3576 {
3577 if (trans_color == 0)
3578 png_error(pp, "validate: unexpected png_get_tRNS (color) result");
3579
3580 switch (dp->colour_type)
3581 {
3582 case 0:
3583 dp->transparent.red = dp->transparent.green = dp->transparent.blue =
3584 trans_color->gray;
3585 dp->is_transparent = 1;
3586 break;
3587
3588 case 2:
3589 dp->transparent.red = trans_color->red;
3590 dp->transparent.green = trans_color->green;
3591 dp->transparent.blue = trans_color->blue;
3592 dp->is_transparent = 1;
3593 break;
3594
3595 case 3:
3596 /* Not expected because it should result in the array case
3597 * above.
3598 */
3599 png_error(pp, "validate: unexpected png_get_tRNS result");
3600 break;
3601
3602 default:
3603 png_error(pp, "validate: invalid tRNS chunk with alpha image");
3604 }
3605 }
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003606 }
3607
3608 /* Read the number of passes - expected to match the value used when
3609 * creating the image (interlaced or not). This has the side effect of
John Bowler660c6e42010-12-19 06:22:23 -06003610 * turning on interlace handling (if do_interlace is not set.)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003611 */
John Bowler660c6e42010-12-19 06:22:23 -06003612 dp->npasses = npasses_from_interlace_type(pp, dp->interlace_type);
3613 if (!dp->do_interlace && dp->npasses != png_set_interlace_handling(pp))
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003614 png_error(pp, "validate: file changed interlace type");
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003615
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003616 /* Caller calls png_read_update_info or png_start_read_image now, then calls
3617 * part2.
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003618 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003619}
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003620
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003621/* This must be called *after* the png_read_update_info call to get the correct
3622 * 'rowbytes' value, otherwise png_get_rowbytes will refer to the untransformed
3623 * image.
3624 */
3625static void
3626standard_info_part2(standard_display *dp, png_structp pp, png_infop pi,
3627 int nImages)
3628{
3629 /* Record cbRow now that it can be found. */
John Bowler660c6e42010-12-19 06:22:23 -06003630 dp->pixel_size = bit_size(pp, png_get_color_type(pp, pi),
3631 png_get_bit_depth(pp, pi));
3632 dp->bit_width = png_get_image_width(pp, pi) * dp->pixel_size;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003633 dp->cbRow = png_get_rowbytes(pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003634
John Bowler660c6e42010-12-19 06:22:23 -06003635 /* Validate the rowbytes here again. */
3636 if (dp->cbRow != (dp->bit_width+7)/8)
3637 png_error(pp, "bad png_get_rowbytes calculation");
3638
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003639 /* Then ensure there is enough space for the output image(s). */
John Bowler9994f252011-05-15 18:52:39 -05003640 store_ensure_image(dp->ps, pp, nImages, dp->cbRow, dp->h);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003641}
3642
3643static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003644standard_info_imp(standard_display *dp, png_structp pp, png_infop pi,
3645 int nImages)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003646{
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003647 /* Note that the validation routine has the side effect of turning on
3648 * interlace handling in the subsequent code.
3649 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003650 standard_info_part1(dp, pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003651
3652 /* And the info callback has to call this (or png_read_update_info - see
3653 * below in the png_modifier code for that variant.
3654 */
3655 png_start_read_image(pp);
3656
3657 /* Validate the height, width and rowbytes plus ensure that sufficient buffer
3658 * exists for decoding the image.
3659 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003660 standard_info_part2(dp, pp, pi, nImages);
3661}
3662
3663static void
3664standard_info(png_structp pp, png_infop pi)
3665{
3666 standard_display *dp = png_get_progressive_ptr(pp);
3667
3668 /* Call with nImages==1 because the progressive reader can only produce one
3669 * image.
3670 */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003671 standard_info_imp(dp, pp, pi, 1 /*only one image*/);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003672}
3673
3674static void
3675progressive_row(png_structp pp, png_bytep new_row, png_uint_32 y, int pass)
3676{
John Bowler660c6e42010-12-19 06:22:23 -06003677 PNG_CONST standard_display *dp = png_get_progressive_ptr(pp);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003678
3679 /* When handling interlacing some rows will be absent in each pass, the
John Bowler660c6e42010-12-19 06:22:23 -06003680 * callback still gets called, but with a NULL pointer. This is checked
3681 * in the 'else' clause below. We need our own 'cbRow', but we can't call
3682 * png_get_rowbytes because we got no info structure.
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003683 */
3684 if (new_row != NULL)
3685 {
John Bowler660c6e42010-12-19 06:22:23 -06003686 png_bytep row;
3687
3688 /* In the case where the reader doesn't do the interlace it gives
3689 * us the y in the sub-image:
3690 */
3691 if (dp->do_interlace && dp->interlace_type == PNG_INTERLACE_ADAM7)
John Bowler5432c012011-02-12 08:59:17 -06003692 {
John Bowler4a12f4a2011-04-17 18:34:22 -05003693#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
John Bowler9616ad92011-02-16 06:13:46 -06003694 /* Use this opportunity to validate the png 'current' APIs: */
John Bowler5432c012011-02-12 08:59:17 -06003695 if (y != png_get_current_row_number(pp))
3696 png_error(pp, "png_get_current_row_number is broken");
John Bowler9616ad92011-02-16 06:13:46 -06003697
3698 if (pass != png_get_current_pass_number(pp))
3699 png_error(pp, "png_get_current_pass_number is broken");
John Bowler4a12f4a2011-04-17 18:34:22 -05003700#endif
John Bowler9616ad92011-02-16 06:13:46 -06003701
3702 y = PNG_ROW_FROM_PASS_ROW(y, pass);
John Bowler5432c012011-02-12 08:59:17 -06003703 }
John Bowler660c6e42010-12-19 06:22:23 -06003704
3705 /* Validate this just in case. */
3706 if (y >= dp->h)
3707 png_error(pp, "invalid y to progressive row callback");
3708
John Bowler9994f252011-05-15 18:52:39 -05003709 row = store_image_row(dp->ps, pp, 0, y);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003710
3711 /* Combine the new row into the old: */
John Bowler660c6e42010-12-19 06:22:23 -06003712 if (dp->do_interlace)
3713 {
3714 if (dp->interlace_type == PNG_INTERLACE_ADAM7)
3715 deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass);
3716 else
3717 memcpy(row, new_row, dp->cbRow);
3718 }
3719 else
3720 png_progressive_combine_row(pp, row, new_row);
3721 } else if (dp->interlace_type == PNG_INTERLACE_ADAM7 &&
3722 PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
3723 PNG_PASS_COLS(dp->w, pass) > 0)
3724 png_error(pp, "missing row in progressive de-interlacing");
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003725}
3726
3727static void
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003728sequential_row(standard_display *dp, png_structp pp, png_infop pi,
John Bowler9994f252011-05-15 18:52:39 -05003729 PNG_CONST int iImage, PNG_CONST int iDisplay)
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003730{
3731 PNG_CONST int npasses = dp->npasses;
John Bowler660c6e42010-12-19 06:22:23 -06003732 PNG_CONST int do_interlace = dp->do_interlace &&
3733 dp->interlace_type == PNG_INTERLACE_ADAM7;
3734 PNG_CONST png_uint_32 height = standard_height(pp, dp->id);
3735 PNG_CONST png_uint_32 width = standard_width(pp, dp->id);
John Bowler9994f252011-05-15 18:52:39 -05003736 PNG_CONST png_store* ps = dp->ps;
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003737 int pass;
3738
John Bowler660c6e42010-12-19 06:22:23 -06003739 for (pass=0; pass<npasses; ++pass)
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003740 {
3741 png_uint_32 y;
John Bowler660c6e42010-12-19 06:22:23 -06003742 png_uint_32 wPass = PNG_PASS_COLS(width, pass);
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003743
John Bowler660c6e42010-12-19 06:22:23 -06003744 for (y=0; y<height; ++y)
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003745 {
John Bowler660c6e42010-12-19 06:22:23 -06003746 if (do_interlace)
3747 {
3748 /* wPass may be zero or this row may not be in this pass.
3749 * png_read_row must not be called in either case.
3750 */
3751 if (wPass > 0 && PNG_ROW_IN_INTERLACE_PASS(y, pass))
3752 {
3753 /* Read the row into a pair of temporary buffers, then do the
3754 * merge here into the output rows.
3755 */
3756 png_byte row[STANDARD_ROWMAX], display[STANDARD_ROWMAX];
3757
3758 /* The following aids (to some extent) error detection - we can
3759 * see where png_read_row wrote. Use opposite values in row and
3760 * display to make this easier.
3761 */
3762 memset(row, 0xff, sizeof row);
3763 memset(display, 0, sizeof display);
3764
3765 png_read_row(pp, row, display);
3766
John Bowler9994f252011-05-15 18:52:39 -05003767 if (iImage >= 0)
3768 deinterlace_row(store_image_row(ps, pp, iImage, y), row,
3769 dp->pixel_size, dp->w, pass);
John Bowler660c6e42010-12-19 06:22:23 -06003770
John Bowler9994f252011-05-15 18:52:39 -05003771 if (iDisplay >= 0)
3772 deinterlace_row(store_image_row(ps, pp, iDisplay, y), display,
3773 dp->pixel_size, dp->w, pass);
John Bowler660c6e42010-12-19 06:22:23 -06003774 }
3775 }
3776 else
John Bowler9994f252011-05-15 18:52:39 -05003777 png_read_row(pp,
3778 iImage >= 0 ? store_image_row(ps, pp, iImage, y) : NULL,
3779 iDisplay >= 0 ? store_image_row(ps, pp, iDisplay, y) : NULL);
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003780 }
3781 }
3782
3783 /* And finish the read operation (only really necessary if the caller wants
3784 * to find additional data in png_info from chunks after the last IDAT.)
3785 */
3786 png_read_end(pp, pi);
3787}
3788
3789static void
John Bowler9994f252011-05-15 18:52:39 -05003790standard_row_validate(standard_display *dp, png_structp pp,
3791 int iImage, int iDisplay, png_uint_32 y)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003792{
John Bowler660c6e42010-12-19 06:22:23 -06003793 png_byte std[STANDARD_ROWMAX];
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003794
John Bowler660c6e42010-12-19 06:22:23 -06003795 memset(std, 0xff, sizeof std);
3796 standard_row(pp, std, dp->id, y);
3797
3798 /* At the end both the 'row' and 'display' arrays should end up identical.
3799 * In earlier passes 'row' will be partially filled in, with only the pixels
3800 * that have been read so far, but 'display' will have those pixels
3801 * replicated to fill the unread pixels while reading an interlaced image.
3802 * The side effect inside the libpng sequential reader is that the 'row'
3803 * array retains the correct values for unwritten pixels within the row
3804 * bytes, while the 'display' array gets bits off the end of the image (in
3805 * the last byte) trashed. Unfortunately in the progressive reader the
3806 * row bytes are always trashed, so we always do a pixel_cmp here even though
3807 * a memcmp of all cbRow bytes will succeed for the sequential reader.
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003808 */
John Bowler9994f252011-05-15 18:52:39 -05003809 if (iImage >= 0 && pixel_cmp(std, store_image_row(dp->ps, pp, iImage, y),
3810 dp->bit_width) != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003811 {
3812 char msg[64];
3813 sprintf(msg, "PNG image row %d changed", y);
3814 png_error(pp, msg);
3815 }
3816
John Bowler660c6e42010-12-19 06:22:23 -06003817 /* In this case use pixel_cmp because we need to compare a partial
3818 * byte at the end of the row if the row is not an exact multiple
3819 * of 8 bits wide.
3820 */
John Bowler9994f252011-05-15 18:52:39 -05003821 if (iDisplay >= 0 && pixel_cmp(std, store_image_row(dp->ps, pp, iDisplay, y),
3822 dp->bit_width) != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003823 {
3824 char msg[64];
3825 sprintf(msg, "display row %d changed", y);
3826 png_error(pp, msg);
3827 }
3828}
3829
3830static void
John Bowler9994f252011-05-15 18:52:39 -05003831standard_image_validate(standard_display *dp, png_structp pp, int iImage,
3832 int iDisplay)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003833{
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003834 png_uint_32 y;
3835
John Bowler9994f252011-05-15 18:52:39 -05003836 if (iImage >= 0)
3837 store_image_check(dp->ps, pp, iImage);
3838
3839 if (iDisplay >= 0)
3840 store_image_check(dp->ps, pp, iDisplay);
3841
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003842 for (y=0; y<dp->h; ++y)
John Bowler9994f252011-05-15 18:52:39 -05003843 standard_row_validate(dp, pp, iImage, iDisplay, y);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003844
3845 /* This avoids false positives if the validation code is never called! */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003846 dp->ps->validated = 1;
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003847}
3848
3849static void
3850standard_end(png_structp pp, png_infop pi)
3851{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003852 standard_display *dp = png_get_progressive_ptr(pp);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003853
John Bowlerafea7d12011-01-28 06:38:14 -06003854 UNUSED(pi)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003855
3856 /* Validate the image - progressive reading only produces one variant for
3857 * interlaced images.
3858 */
John Bowler9994f252011-05-15 18:52:39 -05003859 standard_image_validate(dp, pp, 0, -1);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003860}
3861
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003862/* A single test run checking the standard image to ensure it is not damaged. */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003863static void
John Bowler660c6e42010-12-19 06:22:23 -06003864standard_test(png_store* PNG_CONST psIn, png_uint_32 PNG_CONST id,
3865 int do_interlace)
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003866{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003867 standard_display d;
3868 context(psIn, fault);
3869
3870 /* Set up the display (stack frame) variables from the arguments to the
3871 * function and initialize the locals that are filled in later.
3872 */
John Bowler660c6e42010-12-19 06:22:23 -06003873 standard_display_init(&d, psIn, id, do_interlace);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003874
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003875 /* Everything is protected by a Try/Catch. The functions called also
3876 * typically have local Try/Catch blocks.
3877 */
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003878 Try
3879 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003880 png_structp pp;
3881 png_infop pi;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003882
John Bowler660c6e42010-12-19 06:22:23 -06003883 /* Get a png_struct for reading the image. This will throw an error if it
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05003884 * fails, so we don't need to check the result.
3885 */
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003886 pp = set_store_for_read(d.ps, &pi, d.id,
John Bowler660c6e42010-12-19 06:22:23 -06003887 d.do_interlace ? (d.ps->progressive ?
3888 "pngvalid progressive deinterlacer" :
3889 "pngvalid sequential deinterlacer") : (d.ps->progressive ?
3890 "progressive reader" : "sequential reader"));
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003891
John Bowler9994f252011-05-15 18:52:39 -05003892 /* Initialize the palette correctly from the png_store_file. */
3893 standard_palette_init(&d);
3894
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003895 /* Introduce the correct read function. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003896 if (d.ps->progressive)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003897 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003898 png_set_progressive_read_fn(pp, &d, standard_info, progressive_row,
3899 standard_end);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003900
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003901 /* Now feed data into the reader until we reach the end: */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003902 store_progressive_read(d.ps, pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003903 }
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003904 else
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003905 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003906 /* Note that this takes the store, not the display. */
3907 png_set_read_fn(pp, d.ps, store_read);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003908
3909 /* Check the header values: */
3910 png_read_info(pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003911
3912 /* The code tests both versions of the images that the sequential
3913 * reader can produce.
3914 */
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06003915 standard_info_imp(&d, pp, pi, 2 /*images*/);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003916
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003917 /* Need the total bytes in the image below; we can't get to this point
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003918 * unless the PNG file values have been checked against the expected
3919 * values.
3920 */
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003921 {
John Bowler9994f252011-05-15 18:52:39 -05003922 sequential_row(&d, pp, pi, 0, 1);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003923
3924 /* After the last pass loop over the rows again to check that the
3925 * image is correct.
3926 */
John Bowler9994f252011-05-15 18:52:39 -05003927 if (!d.speed)
3928 standard_image_validate(&d, pp, 0, 1);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003929 }
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003930 }
3931
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003932 /* Check for validation. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003933 if (!d.ps->validated)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003934 png_error(pp, "image read failed silently");
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003935
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003936 /* Successful completion. */
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003937 }
3938
3939 Catch(fault)
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003940 d.ps = fault; /* make sure this hasn't been clobbered. */
3941
3942 /* In either case clean up the store. */
3943 store_read_reset(d.ps);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003944}
3945
3946static int
3947test_standard(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003948 int bdlo, int PNG_CONST bdhi)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003949{
3950 for (; bdlo <= bdhi; ++bdlo)
3951 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003952 int interlace_type;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003953
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003954 for (interlace_type = PNG_INTERLACE_NONE;
3955 interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
3956 {
John Bowler9994f252011-05-15 18:52:39 -05003957 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06003958 interlace_type, 0, 0, 0), 0/*do_interlace*/);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003959
3960 if (fail(pm))
3961 return 0;
3962 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003963 }
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003964
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003965 return 1; /* keep going */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003966}
3967
3968static void
3969perform_standard_test(png_modifier *pm)
3970{
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003971 /* Test each colour type over the valid range of bit depths (expressed as
3972 * log2(bit_depth) in turn, stop as soon as any error is detected.
3973 */
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003974 if (!test_standard(pm, 0, 0, READ_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003975 return;
3976
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003977 if (!test_standard(pm, 2, 3, READ_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003978 return;
3979
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003980 if (!test_standard(pm, 3, 0, 3))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003981 return;
3982
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003983 if (!test_standard(pm, 4, 3, READ_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003984 return;
3985
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003986 if (!test_standard(pm, 6, 3, READ_BDHI))
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003987 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003988}
3989
3990
John Bowler660c6e42010-12-19 06:22:23 -06003991/********************************** SIZE TESTS ********************************/
3992static int
3993test_size(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
3994 int bdlo, int PNG_CONST bdhi)
3995{
3996 /* Run the tests on each combination.
3997 *
3998 * NOTE: on my 32 bit x86 each of the following blocks takes
3999 * a total of 3.5 seconds if done across every combo of bit depth
4000 * width and height. This is a waste of time in practice, hence the
4001 * hinc and winc stuff:
4002 */
4003 static PNG_CONST png_byte hinc[] = {1, 3, 11, 1, 5};
4004 static PNG_CONST png_byte winc[] = {1, 9, 5, 7, 1};
4005 for (; bdlo <= bdhi; ++bdlo)
4006 {
4007 png_uint_32 h, w;
4008
4009 for (h=1; h<=16; h+=hinc[bdlo]) for (w=1; w<=16; w+=winc[bdlo])
4010 {
4011 /* First test all the 'size' images against the sequential
4012 * reader using libpng to deinterlace (where required.) This
4013 * validates the write side of libpng. There are four possibilities
4014 * to validate.
4015 */
John Bowler9994f252011-05-15 18:52:39 -05004016 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004017 PNG_INTERLACE_NONE, w, h, 0), 0/*do_interlace*/);
4018
4019 if (fail(pm))
4020 return 0;
4021
John Bowler9994f252011-05-15 18:52:39 -05004022 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004023 PNG_INTERLACE_NONE, w, h, 1), 0/*do_interlace*/);
4024
4025 if (fail(pm))
4026 return 0;
4027
John Bowler9994f252011-05-15 18:52:39 -05004028 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004029 PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/);
4030
4031 if (fail(pm))
4032 return 0;
4033
John Bowler9994f252011-05-15 18:52:39 -05004034 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004035 PNG_INTERLACE_ADAM7, w, h, 1), 0/*do_interlace*/);
4036
4037 if (fail(pm))
4038 return 0;
4039
4040 /* Now validate the interlaced read side - do_interlace true,
4041 * in the progressive case this does actually make a difference
4042 * to the code used in the non-interlaced case too.
4043 */
John Bowler9994f252011-05-15 18:52:39 -05004044 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004045 PNG_INTERLACE_NONE, w, h, 0), 1/*do_interlace*/);
4046
4047 if (fail(pm))
4048 return 0;
4049
John Bowler9994f252011-05-15 18:52:39 -05004050 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004051 PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/);
4052
4053 if (fail(pm))
4054 return 0;
4055 }
4056 }
4057
4058 return 1; /* keep going */
4059}
4060
4061static void
4062perform_size_test(png_modifier *pm)
4063{
4064 /* Test each colour type over the valid range of bit depths (expressed as
4065 * log2(bit_depth) in turn, stop as soon as any error is detected.
4066 */
4067 if (!test_size(pm, 0, 0, READ_BDHI))
4068 return;
4069
4070 if (!test_size(pm, 2, 3, READ_BDHI))
4071 return;
4072
4073 /* For the moment don't do the palette test - it's a waste of time when
4074 * compared to the greyscale test.
4075 */
4076#if 0
4077 if (!test_size(pm, 3, 0, 3))
4078 return;
4079#endif
4080
4081 if (!test_size(pm, 4, 3, READ_BDHI))
4082 return;
4083
4084 if (!test_size(pm, 6, 3, READ_BDHI))
4085 return;
4086}
4087
4088
John Bowlerf21a0d02011-01-23 23:55:19 -06004089/******************************* TRANSFORM TESTS ******************************/
John Bowler4a12f4a2011-04-17 18:34:22 -05004090#ifdef PNG_READ_TRANSFORMS_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06004091/* A set of tests to validate libpng image transforms. The possibilities here
4092 * are legion because the transforms can be combined in a combinatorial
4093 * fashion. To deal with this some measure of restraint is required, otherwise
4094 * the tests would take forever.
4095 */
4096typedef struct image_pixel
4097{
4098 /* A local (pngvalid) representation of a PNG pixel, in all its
4099 * various forms.
4100 */
4101 unsigned int red, green, blue, alpha; /* For non-palette images. */
4102 unsigned int palette_index; /* For a palette image. */
4103 png_byte colour_type; /* As in the spec. */
John Bowlerafea7d12011-01-28 06:38:14 -06004104 png_byte bit_depth; /* Defines bit size in row */
4105 png_byte sample_depth; /* Scale of samples */
John Bowlerf21a0d02011-01-23 23:55:19 -06004106 int have_tRNS; /* tRNS chunk may need processing */
4107
4108 /* For checking the code calculates double precision floating point values
4109 * along with an error value, accumulated from the transforms. Because an
4110 * sBIT setting allows larger error bounds (indeed, by the spec, apparently
4111 * up to just less than +/-1 in the scaled value) the *lowest* sBIT for each
4112 * channel is stored. This sBIT value is folded in to the stored error value
4113 * at the end of the application of the transforms to the pixel.
4114 */
4115 double redf, greenf, bluef, alphaf;
4116 double rede, greene, bluee, alphae;
4117 png_byte red_sBIT, green_sBIT, blue_sBIT, alpha_sBIT;
4118} image_pixel;
4119
4120/* Shared utility function, see below. */
4121static void
4122image_pixel_setf(image_pixel *this, unsigned int max)
4123{
4124 this->redf = this->red / (double)max;
4125 this->greenf = this->green / (double)max;
4126 this->bluef = this->blue / (double)max;
4127 this->alphaf = this->alpha / (double)max;
4128
4129 if (this->red < max)
4130 this->rede = this->redf * DBL_EPSILON;
4131 else
4132 this->rede = 0;
4133 if (this->green < max)
4134 this->greene = this->greenf * DBL_EPSILON;
4135 else
4136 this->greene = 0;
4137 if (this->blue < max)
4138 this->bluee = this->bluef * DBL_EPSILON;
4139 else
4140 this->bluee = 0;
4141 if (this->alpha < max)
4142 this->alphae = this->alphaf * DBL_EPSILON;
4143 else
4144 this->alphae = 0;
4145}
4146
4147/* Initialize the structure for the next pixel - call this before doing any
4148 * transforms and call it for each pixel since all the fields may need to be
4149 * reset.
4150 */
4151static void
4152image_pixel_init(image_pixel *this, png_const_bytep row, png_byte colour_type,
John Bowler9994f252011-05-15 18:52:39 -05004153 png_byte bit_depth, png_uint_32 x, store_palette palette)
John Bowlerf21a0d02011-01-23 23:55:19 -06004154{
John Bowlerafea7d12011-01-28 06:38:14 -06004155 PNG_CONST png_byte sample_depth = (png_byte)(colour_type ==
4156 PNG_COLOR_TYPE_PALETTE ? 8 : bit_depth);
4157 PNG_CONST unsigned int max = (1U<<sample_depth)-1;
John Bowlerf21a0d02011-01-23 23:55:19 -06004158
4159 /* Initially just set everything to the same number and the alpha to opaque.
4160 * Note that this currently assumes a simple palette where entry x has colour
4161 * rgb(x,x,x)!
4162 */
4163 this->palette_index = this->red = this->green = this->blue =
4164 sample(row, colour_type, bit_depth, x, 0);
4165 this->alpha = max;
4166 this->red_sBIT = this->green_sBIT = this->blue_sBIT = this->alpha_sBIT =
John Bowlerafea7d12011-01-28 06:38:14 -06004167 sample_depth;
John Bowlerf21a0d02011-01-23 23:55:19 -06004168
John Bowlerafea7d12011-01-28 06:38:14 -06004169 /* Then override as appropriate: */
4170 if (colour_type == 3) /* palette */
John Bowlerf21a0d02011-01-23 23:55:19 -06004171 {
John Bowlerafea7d12011-01-28 06:38:14 -06004172 /* This permits the caller to default to the sample value. */
4173 if (palette != 0)
4174 {
4175 PNG_CONST unsigned int i = this->palette_index;
4176
4177 this->red = palette[i].red;
4178 this->green = palette[i].green;
4179 this->blue = palette[i].blue;
4180 this->alpha = palette[i].alpha;
4181 }
John Bowlerf21a0d02011-01-23 23:55:19 -06004182 }
John Bowlerafea7d12011-01-28 06:38:14 -06004183
4184 else /* not palette */
4185 {
4186 unsigned int i = 0;
4187
4188 if (colour_type & 2)
4189 {
4190 this->green = sample(row, colour_type, bit_depth, x, 1);
4191 this->blue = sample(row, colour_type, bit_depth, x, 2);
4192 i = 2;
4193 }
4194 if (colour_type & 4)
4195 this->alpha = sample(row, colour_type, bit_depth, x, ++i);
4196 }
John Bowlerf21a0d02011-01-23 23:55:19 -06004197
4198 /* Calculate the scaled values, these are simply the values divided by
4199 * 'max' and the error is initialized to the double precision epsilon value
4200 * from the header file.
4201 */
4202 image_pixel_setf(this, max);
4203
4204 /* Store the input information for use in the transforms - these will
4205 * modify the information.
4206 */
4207 this->colour_type = colour_type;
4208 this->bit_depth = bit_depth;
John Bowlerafea7d12011-01-28 06:38:14 -06004209 this->sample_depth = sample_depth;
John Bowlerf21a0d02011-01-23 23:55:19 -06004210 this->have_tRNS = 0;
4211}
4212
4213/* Convert a palette image to an rgb image. This necessarily converts the tRNS
John Bowler9994f252011-05-15 18:52:39 -05004214 * chunk at the same time, because the tRNS will be in palette form. The way
4215 * palette validation works means that the original palette is never updated,
4216 * instead the image_pixel value from the row contains the RGB of the
4217 * corresponding palette entry and *this* is updated. Consequently this routine
4218 * only needs to change the colour type information.
John Bowlerf21a0d02011-01-23 23:55:19 -06004219 */
4220static void
John Bowler9994f252011-05-15 18:52:39 -05004221image_pixel_convert_PLTE(image_pixel *this)
John Bowlerf21a0d02011-01-23 23:55:19 -06004222{
John Bowlerafea7d12011-01-28 06:38:14 -06004223 if (this->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowlerf21a0d02011-01-23 23:55:19 -06004224 {
John Bowlerf21a0d02011-01-23 23:55:19 -06004225 if (this->have_tRNS)
4226 {
John Bowlerf21a0d02011-01-23 23:55:19 -06004227 this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
4228 this->have_tRNS = 0;
4229 }
4230 else
John Bowlerf21a0d02011-01-23 23:55:19 -06004231 this->colour_type = PNG_COLOR_TYPE_RGB;
John Bowlerf21a0d02011-01-23 23:55:19 -06004232
John Bowler9994f252011-05-15 18:52:39 -05004233 /* The bit depth of the row changes at this point too (notice that this is
4234 * the row format, not the sample depth, which is separate.)
John Bowlerf21a0d02011-01-23 23:55:19 -06004235 */
John Bowler9994f252011-05-15 18:52:39 -05004236 this->bit_depth = 8;
John Bowlerf21a0d02011-01-23 23:55:19 -06004237 }
4238}
4239
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05004240/* Add an alpha channel; this will import the tRNS information because tRNS is
John Bowlerf21a0d02011-01-23 23:55:19 -06004241 * not valid in an alpha image. The bit depth will invariably be set to at
4242 * least 8. Palette images will be converted to alpha (using the above API).
4243 */
4244static void
4245image_pixel_add_alpha(image_pixel *this, const standard_display *display)
4246{
4247 if (this->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05004248 image_pixel_convert_PLTE(this);
John Bowlerf21a0d02011-01-23 23:55:19 -06004249
4250 if ((this->colour_type & PNG_COLOR_MASK_ALPHA) == 0)
4251 {
4252 if (this->colour_type == PNG_COLOR_TYPE_GRAY)
4253 {
4254 if (this->bit_depth < 8)
4255 this->bit_depth = 8;
4256
4257 if (this->have_tRNS)
4258 {
4259 this->have_tRNS = 0;
4260
4261 /* Check the input, original, channel value here against the
4262 * original tRNS gray chunk valie.
4263 */
4264 if (this->red == display->transparent.red)
4265 this->alphaf = 0;
4266 else
4267 this->alphaf = 1;
4268 }
4269 else
4270 this->alphaf = 1;
4271
4272 this->colour_type = PNG_COLOR_TYPE_GRAY_ALPHA;
4273 }
4274
4275 else if (this->colour_type == PNG_COLOR_TYPE_RGB)
4276 {
4277 if (this->have_tRNS)
4278 {
4279 this->have_tRNS = 0;
4280
4281 /* Again, check the exact input values, not the current transformed
4282 * value!
4283 */
4284 if (this->red == display->transparent.red &&
4285 this->green == display->transparent.green &&
4286 this->blue == display->transparent.blue)
4287 this->alphaf = 0;
4288 else
4289 this->alphaf = 1;
4290
4291 this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
4292 }
4293 }
4294
4295 /* The error in the alpha is zero and the sBIT value comes from the
4296 * original sBIT data (actually it will always be the original bit depth).
4297 */
4298 this->alphae = 0;
4299 this->alpha_sBIT = display->alpha_sBIT;
4300 }
4301}
4302
4303struct transform_display;
4304typedef struct image_transform
4305{
John Bowlerafea7d12011-01-28 06:38:14 -06004306 /* The name of this transform: a string. */
4307 PNG_CONST char *name;
4308
4309 /* Each transform can be disabled from the command line: */
4310 int enable;
4311
John Bowlerf21a0d02011-01-23 23:55:19 -06004312 /* The global list of transforms; read only. */
4313 struct image_transform *PNG_CONST list;
4314
4315 /* The global count of the number of times this transform has been set on an
4316 * image.
4317 */
4318 unsigned int global_use;
4319
4320 /* The local count of the number of times this transform has been set. */
4321 unsigned int local_use;
4322
4323 /* The next transform in the list, each transform must call its own next
4324 * transform after it has processed the pixel successfully.
4325 */
4326 PNG_CONST struct image_transform *next;
4327
4328 /* A single transform for the image, expressed as a series of function
4329 * callbacks and some space for values.
4330 *
4331 * First a callback to set the transform on the current png_read_struct:
4332 */
4333 void (*set)(PNG_CONST struct image_transform *this,
4334 struct transform_display *that, png_structp pp, png_infop pi);
4335
4336 /* Then a transform that takes an input pixel in one PNG format or another
4337 * and modifies it by a pngvalid implementation of the transform (thus
4338 * duplicating the libpng intent without, we hope, duplicating the bugs
4339 * in the libpng implementation!) The png_structp is solely to allow error
4340 * reporting via png_error and png_warning.
4341 */
4342 void (*mod)(PNG_CONST struct image_transform *this, image_pixel *that,
4343 png_structp pp, PNG_CONST struct transform_display *display);
4344
4345 /* Add this transform to the list and return true if the transform is
4346 * meaningful for this colour type and bit depth - if false then the
4347 * transform should have no effect on the image so there's not a lot of
4348 * point running it.
4349 */
4350 int (*add)(struct image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06004351 PNG_CONST struct image_transform **that, png_byte colour_type,
John Bowlerf21a0d02011-01-23 23:55:19 -06004352 png_byte bit_depth);
4353} image_transform;
4354
4355typedef struct transform_display
4356{
4357 standard_display this;
4358
4359 /* Parameters */
4360 png_modifier* pm;
4361 PNG_CONST image_transform* transform_list;
4362
4363 /* Local variables */
4364 png_byte output_colour_type;
4365 png_byte output_bit_depth;
John Bowler9994f252011-05-15 18:52:39 -05004366
4367 /* Variables for the individual transforms. */
4368 /* png_set_background */
4369 image_pixel background_colour;
John Bowlerf21a0d02011-01-23 23:55:19 -06004370} transform_display;
4371
4372/* Two functions to end the list: */
4373static void
4374image_transform_set_end(PNG_CONST image_transform *this,
4375 transform_display *that, png_structp pp, png_infop pi)
4376{
John Bowlerafea7d12011-01-28 06:38:14 -06004377 UNUSED(this)
4378 UNUSED(that)
4379 UNUSED(pp)
4380 UNUSED(pi)
John Bowlerf21a0d02011-01-23 23:55:19 -06004381}
4382
4383/* At the end of the list recalculate the output image pixel value from the
4384 * double precision values set up by the preceding 'mod' calls:
4385 */
4386static unsigned int
4387sample_scale(double sample_value, unsigned int scale)
4388{
4389 sample_value = floor(sample_value * scale + .5);
4390
4391 /* Return NaN as 0: */
4392 if (!(sample_value > 0))
4393 sample_value = 0;
4394 else if (sample_value > scale)
4395 sample_value = scale;
4396
4397 return (unsigned int)sample_value;
4398}
4399
4400static void
4401image_transform_mod_end(PNG_CONST image_transform *this, image_pixel *that,
4402 png_structp pp, PNG_CONST transform_display *display)
4403{
John Bowlerafea7d12011-01-28 06:38:14 -06004404 PNG_CONST unsigned int scale = (1U<<that->sample_depth)-1;
John Bowlerf21a0d02011-01-23 23:55:19 -06004405
John Bowlerafea7d12011-01-28 06:38:14 -06004406 UNUSED(this)
4407 UNUSED(pp)
4408 UNUSED(display)
John Bowlerf21a0d02011-01-23 23:55:19 -06004409
John Bowlerafea7d12011-01-28 06:38:14 -06004410 /* At the end recalculate the digitized red green and blue values according
4411 * to the current sample_depth of the pixel.
4412 *
4413 * The sample value is simply scaled to the maximum, checking for over
John Bowlerf21a0d02011-01-23 23:55:19 -06004414 * and underflow (which can both happen for some image transforms,
4415 * including simple size scaling, though libpng doesn't do that at present.
4416 */
4417 that->red = sample_scale(that->redf, scale);
4418
4419 /* The error value is increased, at the end, according to the lowest sBIT
4420 * value seen. Common sense tells us that the intermediate integer
4421 * representations are no more accurate than +/- 0.5 in the integral values,
4422 * the sBIT allows the implementation to be worse than this. In addition the
4423 * PNG specification actually permits any error within the range (-1..+1),
John Bowlerafea7d12011-01-28 06:38:14 -06004424 * but that is ignored here. Instead the final digitized value is compared,
4425 * below to the digitized value of the error limits - this has the net effect
4426 * of allowing (almost) +/-1 in the output value. It's difficult to see how
4427 * any algorithm that digitizes intermediate results can be more accurate.
John Bowlerf21a0d02011-01-23 23:55:19 -06004428 */
4429 that->rede += 1./(2*((1U<<that->red_sBIT)-1));
4430
4431 if (that->colour_type & PNG_COLOR_MASK_COLOR)
4432 {
4433 that->green = sample_scale(that->greenf, scale);
4434 that->blue = sample_scale(that->bluef, scale);
4435 that->greene += 1./(2*((1U<<that->green_sBIT)-1));
4436 that->bluee += 1./(2*((1U<<that->blue_sBIT)-1));
4437 }
4438 else
4439 {
4440 that->blue = that->green = that->red;
4441 that->bluef = that->greenf = that->redf;
4442 that->bluee = that->greene = that->rede;
4443 }
4444
John Bowlerafea7d12011-01-28 06:38:14 -06004445 if ((that->colour_type & PNG_COLOR_MASK_ALPHA) ||
4446 that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowlerf21a0d02011-01-23 23:55:19 -06004447 {
4448 that->alpha = sample_scale(that->alphaf, scale);
4449 that->alphae += 1./(2*((1U<<that->alpha_sBIT)-1));
4450 }
4451 else
4452 {
4453 that->alpha = scale; /* opaque */
4454 that->alpha = 1; /* Override this. */
4455 that->alphae = 0; /* It's exact ;-) */
4456 }
4457}
4458
4459/* Static 'end' structure: */
4460static image_transform image_transform_end =
4461{
John Bowlerafea7d12011-01-28 06:38:14 -06004462 "(end)", /* name */
4463 1, /* enable */
John Bowlerf21a0d02011-01-23 23:55:19 -06004464 0, /* list */
4465 0, /* global_use */
4466 0, /* local_use */
4467 0, /* next */
4468 image_transform_set_end,
4469 image_transform_mod_end,
4470 0 /* never called, I want it to crash if it is! */
4471};
4472
4473/* Reader callbacks and implementations, where they differ from the standard
4474 * ones.
4475 */
4476static void
4477transform_display_init(transform_display *dp, png_modifier *pm, png_uint_32 id,
4478 PNG_CONST image_transform *transform_list)
4479{
4480 /* Standard fields */
4481 standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/);
4482
4483 /* Parameter fields */
4484 dp->pm = pm;
4485 dp->transform_list = transform_list;
4486
4487 /* Local variable fields */
4488 dp->output_colour_type = 255; /* invalid */
4489 dp->output_bit_depth = 255; /* invalid */
4490}
4491
4492static void
4493transform_info_imp(transform_display *dp, png_structp pp, png_infop pi)
4494{
4495 /* Reuse the standard stuff as appropriate. */
4496 standard_info_part1(&dp->this, pp, pi);
4497
4498 /* Now set the list of transforms. */
4499 dp->transform_list->set(dp->transform_list, dp, pp, pi);
4500
4501 /* Update the info structure for these transforms: */
4502 png_read_update_info(pp, pi);
4503
4504 /* And get the output information into the standard_display */
4505 standard_info_part2(&dp->this, pp, pi, 1/*images*/);
4506
4507 /* Plus the extra stuff we need for the transform tests: */
4508 dp->output_colour_type = png_get_color_type(pp, pi);
4509 dp->output_bit_depth = png_get_bit_depth(pp, pi);
4510
4511 /* Validate the combination of colour type and bit depth that we are getting
4512 * out of libpng; the semantics of something not in the PNG spec are, at
4513 * best, unclear.
4514 */
4515 switch (dp->output_colour_type)
4516 {
4517 case PNG_COLOR_TYPE_PALETTE:
4518 if (dp->output_bit_depth > 8) goto error;
4519 /*FALL THROUGH*/
4520 case PNG_COLOR_TYPE_GRAY:
4521 if (dp->output_bit_depth == 1 || dp->output_bit_depth == 2 ||
4522 dp->output_bit_depth == 4)
4523 break;
4524 /*FALL THROUGH*/
4525 default:
4526 if (dp->output_bit_depth == 8 || dp->output_bit_depth == 16)
4527 break;
4528 /*FALL THROUGH*/
4529 error:
4530 {
4531 char message[128];
4532 size_t pos;
4533
4534 pos = safecat(message, sizeof message, 0,
4535 "invalid final bit depth: colour type(");
4536 pos = safecatn(message, sizeof message, pos, dp->output_colour_type);
4537 pos = safecat(message, sizeof message, pos, ") with bit depth: ");
4538 pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
4539
4540 png_error(pp, message);
4541 }
4542 }
4543
4544 /* Use a test pixel to check that the output agrees with what we expect -
4545 * this avoids running the whole test if the output is unexpected.
4546 */
4547 {
4548 image_pixel test_pixel;
4549
4550 memset(&test_pixel, 0, sizeof test_pixel);
4551 test_pixel.colour_type = dp->this.colour_type; /* input */
4552 test_pixel.bit_depth = dp->this.bit_depth;
John Bowlerafea7d12011-01-28 06:38:14 -06004553 if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE)
4554 test_pixel.sample_depth = 8;
4555 else
4556 test_pixel.sample_depth = test_pixel.bit_depth;
John Bowlerf21a0d02011-01-23 23:55:19 -06004557 /* Don't need sBIT here */
4558 test_pixel.have_tRNS = dp->this.is_transparent;
4559
4560 dp->transform_list->mod(dp->transform_list, &test_pixel, pp, dp);
4561
4562 if (test_pixel.colour_type != dp->output_colour_type)
4563 {
4564 char message[128];
4565 size_t pos = safecat(message, sizeof message, 0, "colour type ");
4566
4567 pos = safecatn(message, sizeof message, pos, dp->output_colour_type);
4568 pos = safecat(message, sizeof message, pos, " expected ");
4569 pos = safecatn(message, sizeof message, pos, test_pixel.colour_type);
4570
4571 png_error(pp, message);
John Bowlerf21a0d02011-01-23 23:55:19 -06004572 }
4573
4574 if (test_pixel.bit_depth != dp->output_bit_depth)
4575 {
4576 char message[128];
4577 size_t pos = safecat(message, sizeof message, 0, "bit depth ");
4578
4579 pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
4580 pos = safecat(message, sizeof message, pos, " expected ");
4581 pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth);
4582
4583 png_error(pp, message);
4584 }
John Bowlerafea7d12011-01-28 06:38:14 -06004585
4586 /* If both bit depth and colour type are correct check the sample depth.
4587 * I believe these are both internal errors.
4588 */
4589 if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE)
4590 {
4591 if (test_pixel.sample_depth != 8) /* oops - internal error! */
4592 png_error(pp, "pngvalid: internal: palette sample depth not 8");
4593 }
4594 else if (test_pixel.sample_depth != dp->output_bit_depth)
4595 {
4596 char message[128];
4597 size_t pos = safecat(message, sizeof message, 0,
4598 "internal: sample depth ");
4599
4600 pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
4601 pos = safecat(message, sizeof message, pos, " expected ");
4602 pos = safecatn(message, sizeof message, pos, test_pixel.sample_depth);
4603
4604 png_error(pp, message);
4605 }
John Bowlerf21a0d02011-01-23 23:55:19 -06004606 }
4607}
4608
4609static void
4610transform_info(png_structp pp, png_infop pi)
4611{
4612 transform_info_imp(png_get_progressive_ptr(pp), pp, pi);
4613}
4614
4615static void
John Bowlerafea7d12011-01-28 06:38:14 -06004616transform_range_check(png_structp pp, unsigned int r, unsigned int g,
4617 unsigned int b, unsigned int a, unsigned int in_digitized, double in,
John Bowler5441e182011-05-18 18:57:12 -05004618 unsigned int out, png_byte sample_depth, double err, PNG_CONST char *name,
4619 double digitization_error)
John Bowlerf21a0d02011-01-23 23:55:19 -06004620{
John Bowlerafea7d12011-01-28 06:38:14 -06004621 /* Compare the scaled, digitzed, values of our local calculation (in+-err)
4622 * with the digitized values libpng produced; 'sample_depth' is the actual
4623 * digitization depth of the libpng output colors (the bit depth except for
John Bowler4a12f4a2011-04-17 18:34:22 -05004624 * palette images where it is always 8.) The check on 'err' is to detect
4625 * internal errors in pngvalid itself (the threshold is about 1/255.)
John Bowlerafea7d12011-01-28 06:38:14 -06004626 */
4627 unsigned int max = (1U<<sample_depth)-1;
John Bowler5441e182011-05-18 18:57:12 -05004628 double in_min = ceil((in-err)*max - digitization_error);
4629 double in_max = floor((in+err)*max + digitization_error);
John Bowler4a12f4a2011-04-17 18:34:22 -05004630 if (err > 4E-3 || !(out >= in_min && out <= in_max))
John Bowlerf21a0d02011-01-23 23:55:19 -06004631 {
John Bowlerafea7d12011-01-28 06:38:14 -06004632 char message[256];
John Bowlerf21a0d02011-01-23 23:55:19 -06004633 size_t pos;
4634
4635 pos = safecat(message, sizeof message, 0, name);
John Bowlerafea7d12011-01-28 06:38:14 -06004636 pos = safecat(message, sizeof message, pos, " output value error: rgba(");
4637 pos = safecatn(message, sizeof message, pos, r);
4638 pos = safecat(message, sizeof message, pos, ",");
4639 pos = safecatn(message, sizeof message, pos, g);
4640 pos = safecat(message, sizeof message, pos, ",");
4641 pos = safecatn(message, sizeof message, pos, b);
4642 pos = safecat(message, sizeof message, pos, ",");
4643 pos = safecatn(message, sizeof message, pos, a);
4644 pos = safecat(message, sizeof message, pos, "): ");
4645 pos = safecatn(message, sizeof message, pos, out);
John Bowlerf21a0d02011-01-23 23:55:19 -06004646 pos = safecat(message, sizeof message, pos, " expected: ");
John Bowlerafea7d12011-01-28 06:38:14 -06004647 pos = safecatn(message, sizeof message, pos, in_digitized);
4648 pos = safecat(message, sizeof message, pos, " (");
4649 pos = safecatd(message, sizeof message, pos, (in-err)*max, 3);
John Bowlerf21a0d02011-01-23 23:55:19 -06004650 pos = safecat(message, sizeof message, pos, "..");
John Bowlerafea7d12011-01-28 06:38:14 -06004651 pos = safecatd(message, sizeof message, pos, (in+err)*max, 3);
4652 pos = safecat(message, sizeof message, pos, ")");
John Bowlerf21a0d02011-01-23 23:55:19 -06004653
4654 png_error(pp, message);
4655 }
4656}
4657
4658static void
John Bowler9994f252011-05-15 18:52:39 -05004659transform_image_validate(transform_display *dp, png_structp pp, png_infop pi)
John Bowlerf21a0d02011-01-23 23:55:19 -06004660{
John Bowlerf21a0d02011-01-23 23:55:19 -06004661 /* Constants for the loop below: */
John Bowler9994f252011-05-15 18:52:39 -05004662 PNG_CONST png_store* PNG_CONST ps = dp->this.ps;
John Bowlerf21a0d02011-01-23 23:55:19 -06004663 PNG_CONST png_byte in_ct = dp->this.colour_type;
4664 PNG_CONST png_byte in_bd = dp->this.bit_depth;
4665 PNG_CONST png_uint_32 w = dp->this.w;
4666 PNG_CONST png_uint_32 h = dp->this.h;
John Bowlerf21a0d02011-01-23 23:55:19 -06004667 PNG_CONST png_byte out_ct = dp->output_colour_type;
4668 PNG_CONST png_byte out_bd = dp->output_bit_depth;
John Bowlerafea7d12011-01-28 06:38:14 -06004669 PNG_CONST png_byte sample_depth = (png_byte)(out_ct ==
4670 PNG_COLOR_TYPE_PALETTE ? 8 : out_bd);
John Bowlerf21a0d02011-01-23 23:55:19 -06004671 PNG_CONST png_byte red_sBIT = dp->this.red_sBIT;
4672 PNG_CONST png_byte green_sBIT = dp->this.green_sBIT;
4673 PNG_CONST png_byte blue_sBIT = dp->this.blue_sBIT;
4674 PNG_CONST png_byte alpha_sBIT = dp->this.alpha_sBIT;
4675 PNG_CONST int have_tRNS = dp->this.is_transparent;
John Bowler5441e182011-05-18 18:57:12 -05004676 double digitization_error;
John Bowlerf21a0d02011-01-23 23:55:19 -06004677
John Bowler9994f252011-05-15 18:52:39 -05004678 store_palette out_palette;
John Bowlerf21a0d02011-01-23 23:55:19 -06004679 png_uint_32 y;
4680
John Bowlerafea7d12011-01-28 06:38:14 -06004681 UNUSED(pi)
4682
John Bowler9994f252011-05-15 18:52:39 -05004683 /* Check for row overwrite errors */
4684 store_image_check(dp->this.ps, pp, 0);
4685
John Bowlerafea7d12011-01-28 06:38:14 -06004686 /* Read the palette corresponding to the output if the output colour type
4687 * indicates a palette, othewise set out_palette to garbage.
4688 */
4689 if (out_ct == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05004690 {
4691 /* Validate that the palette count itself has not changed - this is not
4692 * expected.
4693 */
4694 int npalette = (-1);
4695
4696 (void)read_palette(out_palette, &npalette, pp, pi);
4697 if (npalette != dp->this.npalette)
4698 png_error(pp, "unexpected change in palette size");
John Bowler5441e182011-05-18 18:57:12 -05004699
4700 digitization_error = .5;
John Bowler9994f252011-05-15 18:52:39 -05004701 }
John Bowlerafea7d12011-01-28 06:38:14 -06004702 else
John Bowler5441e182011-05-18 18:57:12 -05004703 {
4704 png_byte in_sample_depth;
4705
John Bowlerafea7d12011-01-28 06:38:14 -06004706 memset(out_palette, 0x5e, sizeof out_palette);
4707
John Bowler5441e182011-05-18 18:57:12 -05004708 /* assume-8-bit-calculations means assume that if the input has 8 bit
4709 * (or less) samples and the output has 16 bit samples the calculations
4710 * will be done with 8 bit precision, not 16.
4711 *
4712 * TODO: fix this in libpng; png_set_expand_16 should cause 16 bit
4713 * calculations to be used throughout.
4714 */
4715 if (in_ct == PNG_COLOR_TYPE_PALETTE || in_bd < 16)
4716 in_sample_depth = 8;
4717 else
4718 in_sample_depth = in_bd;
4719
4720 if (sample_depth != 16 || in_sample_depth > 8 ||
4721 !dp->pm->calculations_use_input_precision)
4722 digitization_error = .5;
4723
4724 /* Else errors are at 8 bit precision, scale .5 in 8 bits to the 16 bits:
4725 */
4726 else
4727 digitization_error = .5 * 257;
4728 }
4729
John Bowler9994f252011-05-15 18:52:39 -05004730 for (y=0; y<h; ++y)
John Bowlerf21a0d02011-01-23 23:55:19 -06004731 {
John Bowler9994f252011-05-15 18:52:39 -05004732 png_const_bytep PNG_CONST pRow = store_image_row(ps, pp, 0, y);
John Bowlerf21a0d02011-01-23 23:55:19 -06004733 png_uint_32 x;
4734
4735 /* The original, standard, row pre-transforms. */
4736 png_byte std[STANDARD_ROWMAX];
4737
4738 transform_row(pp, std, in_ct, in_bd, y);
4739
4740 /* Go through each original pixel transforming it and comparing with what
4741 * libpng did to the same pixel.
4742 */
4743 for (x=0; x<w; ++x)
4744 {
4745 image_pixel in_pixel, out_pixel;
John Bowlerafea7d12011-01-28 06:38:14 -06004746 unsigned int r, g, b, a;
John Bowlerf21a0d02011-01-23 23:55:19 -06004747
4748 /* Find out what we think the pixel should be: */
John Bowlerafea7d12011-01-28 06:38:14 -06004749 image_pixel_init(&in_pixel, std, in_ct, in_bd, x, dp->this.palette);
4750
John Bowlerf21a0d02011-01-23 23:55:19 -06004751 in_pixel.red_sBIT = red_sBIT;
4752 in_pixel.green_sBIT = green_sBIT;
4753 in_pixel.blue_sBIT = blue_sBIT;
4754 in_pixel.alpha_sBIT = alpha_sBIT;
4755 in_pixel.have_tRNS = have_tRNS;
4756
John Bowlerafea7d12011-01-28 06:38:14 -06004757 /* For error detection, below. */
4758 r = in_pixel.red;
4759 g = in_pixel.green;
4760 b = in_pixel.blue;
4761 a = in_pixel.alpha;
4762
John Bowlerf21a0d02011-01-23 23:55:19 -06004763 dp->transform_list->mod(dp->transform_list, &in_pixel, pp, dp);
4764
4765 /* Read the output pixel and compare it to what we got, we don't
John Bowlerafea7d12011-01-28 06:38:14 -06004766 * use the error field here, so no need to update sBIT.
John Bowlerf21a0d02011-01-23 23:55:19 -06004767 */
John Bowlerafea7d12011-01-28 06:38:14 -06004768 image_pixel_init(&out_pixel, pRow, out_ct, out_bd, x, out_palette);
John Bowlerf21a0d02011-01-23 23:55:19 -06004769
John Bowlerafea7d12011-01-28 06:38:14 -06004770 /* We don't expect changes to the index here even if the bit depth is
4771 * changed.
4772 */
4773 if (in_ct == PNG_COLOR_TYPE_PALETTE &&
4774 out_ct == PNG_COLOR_TYPE_PALETTE)
John Bowlerf21a0d02011-01-23 23:55:19 -06004775 {
4776 if (in_pixel.palette_index != out_pixel.palette_index)
4777 png_error(pp, "unexpected transformed palette index");
4778 }
4779
John Bowlerafea7d12011-01-28 06:38:14 -06004780 /* Check the colours for palette images too - in fact the palette could
4781 * be separately verified itself in most cases.
4782 */
4783 if (in_pixel.red != out_pixel.red)
4784 transform_range_check(pp, r, g, b, a, in_pixel.red, in_pixel.redf,
John Bowler5441e182011-05-18 18:57:12 -05004785 out_pixel.red, sample_depth, in_pixel.rede, "red/gray",
4786 digitization_error);
John Bowlerf21a0d02011-01-23 23:55:19 -06004787
John Bowlerafea7d12011-01-28 06:38:14 -06004788 if ((out_ct & PNG_COLOR_MASK_COLOR) != 0 &&
4789 in_pixel.green != out_pixel.green)
4790 transform_range_check(pp, r, g, b, a, in_pixel.green,
4791 in_pixel.greenf, out_pixel.green, sample_depth, in_pixel.greene,
John Bowler5441e182011-05-18 18:57:12 -05004792 "green", digitization_error);
John Bowlerf21a0d02011-01-23 23:55:19 -06004793
John Bowlerafea7d12011-01-28 06:38:14 -06004794 if ((out_ct & PNG_COLOR_MASK_COLOR) != 0 &&
4795 in_pixel.blue != out_pixel.blue)
4796 transform_range_check(pp, r, g, b, a, in_pixel.blue, in_pixel.bluef,
John Bowler5441e182011-05-18 18:57:12 -05004797 out_pixel.blue, sample_depth, in_pixel.bluee, "blue",
4798 digitization_error);
John Bowlerf21a0d02011-01-23 23:55:19 -06004799
John Bowlerafea7d12011-01-28 06:38:14 -06004800 if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0 &&
4801 in_pixel.alpha != out_pixel.alpha)
4802 transform_range_check(pp, r, g, b, a, in_pixel.alpha,
4803 in_pixel.alphaf, out_pixel.alpha, sample_depth, in_pixel.alphae,
John Bowler5441e182011-05-18 18:57:12 -05004804 "alpha", digitization_error);
John Bowlerf21a0d02011-01-23 23:55:19 -06004805 } /* pixel (x) loop */
4806 } /* row (y) loop */
4807
4808 /* Record that something was actually checked to avoid a false positive. */
4809 dp->this.ps->validated = 1;
4810}
4811
4812static void
4813transform_end(png_structp pp, png_infop pi)
4814{
4815 transform_display *dp = png_get_progressive_ptr(pp);
4816
John Bowler9994f252011-05-15 18:52:39 -05004817 transform_image_validate(dp, pp, pi);
John Bowlerf21a0d02011-01-23 23:55:19 -06004818}
4819
4820/* A single test run. */
4821static void
4822transform_test(png_modifier *pmIn, PNG_CONST png_uint_32 idIn,
4823 PNG_CONST image_transform* transform_listIn, PNG_CONST char *name)
4824{
4825 transform_display d;
4826 context(&pmIn->this, fault);
4827
4828 transform_display_init(&d, pmIn, idIn, transform_listIn);
4829
4830 Try
4831 {
4832 png_structp pp;
4833 png_infop pi;
4834
John Bowler9994f252011-05-15 18:52:39 -05004835 /* Get a png_struct for reading the image. */
John Bowlerf21a0d02011-01-23 23:55:19 -06004836 pp = set_modifier_for_read(d.pm, &pi, d.this.id, name);
John Bowler9994f252011-05-15 18:52:39 -05004837 standard_palette_init(&d.this);
John Bowlerf21a0d02011-01-23 23:55:19 -06004838
John Bowlerafea7d12011-01-28 06:38:14 -06004839# if 0
4840 /* Logging (debugging only) */
4841 {
4842 char buffer[256];
4843
4844 (void)store_message(&d.pm->this, pp, buffer, sizeof buffer, 0,
4845 "running test");
4846
4847 fprintf(stderr, "%s\n", buffer);
4848 }
4849# endif
4850
John Bowlerf21a0d02011-01-23 23:55:19 -06004851 /* Introduce the correct read function. */
4852 if (d.pm->this.progressive)
4853 {
4854 /* Share the row function with the standard implementation. */
4855 png_set_progressive_read_fn(pp, &d, transform_info, progressive_row,
4856 transform_end);
4857
4858 /* Now feed data into the reader until we reach the end: */
4859 modifier_progressive_read(d.pm, pp, pi);
4860 }
4861 else
4862 {
4863 /* modifier_read expects a png_modifier* */
4864 png_set_read_fn(pp, d.pm, modifier_read);
4865
4866 /* Check the header values: */
4867 png_read_info(pp, pi);
4868
4869 /* Process the 'info' requirements. Only one image is generated */
4870 transform_info_imp(&d, pp, pi);
4871
John Bowler9994f252011-05-15 18:52:39 -05004872 sequential_row(&d.this, pp, pi, -1, 0);
John Bowlerf21a0d02011-01-23 23:55:19 -06004873
John Bowler9994f252011-05-15 18:52:39 -05004874 if (!d.this.speed)
4875 transform_image_validate(&d, pp, pi);
John Bowlerf21a0d02011-01-23 23:55:19 -06004876 }
4877
4878 modifier_reset(d.pm);
4879 }
4880
4881 Catch(fault)
4882 modifier_reset((png_modifier*)fault);
4883}
4884
4885/* The transforms: */
4886#define ITSTRUCT(name) image_transform_##name
John Bowler4a12f4a2011-04-17 18:34:22 -05004887#define IT(name)\
John Bowlerf21a0d02011-01-23 23:55:19 -06004888static image_transform ITSTRUCT(name) =\
4889{\
John Bowlerafea7d12011-01-28 06:38:14 -06004890 #name,\
4891 1, /*enable*/\
John Bowler4a12f4a2011-04-17 18:34:22 -05004892 &PT, /*list*/\
John Bowlerf21a0d02011-01-23 23:55:19 -06004893 0, /*global_use*/\
4894 0, /*local_use*/\
4895 0, /*next*/\
4896 image_transform_png_set_##name##_set,\
4897 image_transform_png_set_##name##_mod,\
4898 image_transform_png_set_##name##_add\
4899}
John Bowler4a12f4a2011-04-17 18:34:22 -05004900#define PT ITSTRUCT(end) /* stores the previous transform */
John Bowlerf21a0d02011-01-23 23:55:19 -06004901
John Bowlerafea7d12011-01-28 06:38:14 -06004902/* To save code: */
John Bowlerf21a0d02011-01-23 23:55:19 -06004903static int
John Bowlerafea7d12011-01-28 06:38:14 -06004904image_transform_default_add(image_transform *this,
4905 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06004906{
John Bowlerafea7d12011-01-28 06:38:14 -06004907 UNUSED(colour_type)
4908 UNUSED(bit_depth)
4909
John Bowlerf21a0d02011-01-23 23:55:19 -06004910 this->next = *that;
4911 *that = this;
4912
John Bowlerafea7d12011-01-28 06:38:14 -06004913 return 1;
John Bowlerf21a0d02011-01-23 23:55:19 -06004914}
4915
John Bowler4a12f4a2011-04-17 18:34:22 -05004916#ifdef PNG_READ_EXPAND_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06004917/* png_set_palette_to_rgb */
4918static void
4919image_transform_png_set_palette_to_rgb_set(PNG_CONST image_transform *this,
4920 transform_display *that, png_structp pp, png_infop pi)
4921{
4922 png_set_palette_to_rgb(pp);
4923 this->next->set(this->next, that, pp, pi);
4924}
4925
4926static void
4927image_transform_png_set_palette_to_rgb_mod(PNG_CONST image_transform *this,
4928 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
4929{
4930 if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05004931 image_pixel_convert_PLTE(that);
John Bowlerf21a0d02011-01-23 23:55:19 -06004932
4933 this->next->mod(this->next, that, pp, display);
4934}
4935
4936static int
4937image_transform_png_set_palette_to_rgb_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06004938 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06004939{
John Bowlerafea7d12011-01-28 06:38:14 -06004940 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06004941
4942 this->next = *that;
4943 *that = this;
4944
John Bowlerf21a0d02011-01-23 23:55:19 -06004945 return colour_type == PNG_COLOR_TYPE_PALETTE;
4946}
4947
John Bowler4a12f4a2011-04-17 18:34:22 -05004948IT(palette_to_rgb);
4949#undef PT
4950#define PT ITSTRUCT(palette_to_rgb)
4951#endif /* PNG_READ_EXPAND_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06004952
John Bowler4a12f4a2011-04-17 18:34:22 -05004953#ifdef PNG_READ_EXPAND_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06004954/* png_set_tRNS_to_alpha */
4955static void
4956image_transform_png_set_tRNS_to_alpha_set(PNG_CONST image_transform *this,
4957 transform_display *that, png_structp pp, png_infop pi)
4958{
4959 png_set_tRNS_to_alpha(pp);
4960 this->next->set(this->next, that, pp, pi);
4961}
4962
4963static void
4964image_transform_png_set_tRNS_to_alpha_mod(PNG_CONST image_transform *this,
4965 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
4966{
John Bowlerafea7d12011-01-28 06:38:14 -06004967 /* LIBPNG BUG: this always forces palette images to RGB. */
4968 if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05004969 image_pixel_convert_PLTE(that);
John Bowlerafea7d12011-01-28 06:38:14 -06004970
John Bowlerf21a0d02011-01-23 23:55:19 -06004971 /* This effectively does an 'expand' only if there is some transparency to
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -05004972 * convert to an alpha channel.
John Bowlerf21a0d02011-01-23 23:55:19 -06004973 */
4974 if (that->have_tRNS)
4975 image_pixel_add_alpha(that, &display->this);
4976
4977 /* LIBPNG BUG: otherwise libpng still expands to 8 bits! */
John Bowlerafea7d12011-01-28 06:38:14 -06004978 else
4979 {
4980 if (that->bit_depth < 8)
4981 that->bit_depth =8;
4982 if (that->sample_depth < 8)
4983 that->sample_depth = 8;
4984 }
John Bowlerf21a0d02011-01-23 23:55:19 -06004985
4986 this->next->mod(this->next, that, pp, display);
4987}
4988
4989static int
4990image_transform_png_set_tRNS_to_alpha_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06004991 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06004992{
John Bowlerafea7d12011-01-28 06:38:14 -06004993 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06004994
4995 this->next = *that;
4996 *that = this;
4997
John Bowlerf21a0d02011-01-23 23:55:19 -06004998 /* We don't know yet whether there will be a tRNS chunk, but we know that
4999 * this transformation should do nothing if there already is an alpha
5000 * channel.
5001 */
5002 return (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
5003}
5004
John Bowler4a12f4a2011-04-17 18:34:22 -05005005IT(tRNS_to_alpha);
5006#undef PT
5007#define PT ITSTRUCT(tRNS_to_alpha)
5008#endif /* PNG_READ_EXPAND_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005009
John Bowler4a12f4a2011-04-17 18:34:22 -05005010#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005011/* png_set_gray_to_rgb */
5012static void
5013image_transform_png_set_gray_to_rgb_set(PNG_CONST image_transform *this,
5014 transform_display *that, png_structp pp, png_infop pi)
5015{
5016 png_set_gray_to_rgb(pp);
5017 this->next->set(this->next, that, pp, pi);
5018}
5019
5020static void
5021image_transform_png_set_gray_to_rgb_mod(PNG_CONST image_transform *this,
5022 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5023{
5024 /* NOTE: we can actually pend the tRNS processing at this point because we
5025 * can correctly recognize the original pixel value even though we have
5026 * mapped the one gray channel to the three RGB ones, but in fact libpng
5027 * doesn't do this, so we don't either.
5028 */
5029 if ((that->colour_type & PNG_COLOR_MASK_COLOR) == 0 && that->have_tRNS)
5030 image_pixel_add_alpha(that, &display->this);
5031
5032 /* Simply expand the bit depth and alter the colour type as required. */
5033 if (that->colour_type == PNG_COLOR_TYPE_GRAY)
5034 {
5035 /* RGB images have a bit depth at least equal to '8' */
5036 if (that->bit_depth < 8)
John Bowlerafea7d12011-01-28 06:38:14 -06005037 that->sample_depth = that->bit_depth = 8;
John Bowlerf21a0d02011-01-23 23:55:19 -06005038
5039 /* And just changing the colour type works here because the green and blue
5040 * channels are being maintained in lock-step with the red/gray:
5041 */
5042 that->colour_type = PNG_COLOR_TYPE_RGB;
5043 }
5044
5045 else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
5046 that->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
5047
5048 this->next->mod(this->next, that, pp, display);
5049}
5050
5051static int
5052image_transform_png_set_gray_to_rgb_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06005053 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005054{
John Bowlerafea7d12011-01-28 06:38:14 -06005055 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005056
5057 this->next = *that;
5058 *that = this;
5059
John Bowlerf21a0d02011-01-23 23:55:19 -06005060 return (colour_type & PNG_COLOR_MASK_COLOR) == 0;
5061}
5062
John Bowler4a12f4a2011-04-17 18:34:22 -05005063IT(gray_to_rgb);
5064#undef PT
5065#define PT ITSTRUCT(gray_to_rgb)
5066#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005067
John Bowler4a12f4a2011-04-17 18:34:22 -05005068#ifdef PNG_READ_EXPAND_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005069/* png_set_expand */
5070static void
5071image_transform_png_set_expand_set(PNG_CONST image_transform *this,
5072 transform_display *that, png_structp pp, png_infop pi)
5073{
5074 png_set_expand(pp);
5075 this->next->set(this->next, that, pp, pi);
5076}
5077
5078static void
5079image_transform_png_set_expand_mod(PNG_CONST image_transform *this,
5080 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5081{
5082 /* The general expand case depends on what the colour type is: */
John Bowlerf21a0d02011-01-23 23:55:19 -06005083 if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05005084 image_pixel_convert_PLTE(that);
John Bowlerafea7d12011-01-28 06:38:14 -06005085 else if (that->bit_depth < 8) /* grayscale */
5086 that->sample_depth = that->bit_depth = 8;
John Bowlerf21a0d02011-01-23 23:55:19 -06005087
5088 if (that->have_tRNS)
5089 image_pixel_add_alpha(that, &display->this);
5090
5091 this->next->mod(this->next, that, pp, display);
5092}
5093
5094static int
5095image_transform_png_set_expand_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06005096 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005097{
John Bowlerafea7d12011-01-28 06:38:14 -06005098 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005099
5100 this->next = *that;
5101 *that = this;
5102
John Bowlerf21a0d02011-01-23 23:55:19 -06005103 /* 'expand' should do nothing for RGBA or GA input - no tRNS and the bit
5104 * depth is at least 8 already.
5105 */
5106 return (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
5107}
5108
John Bowler4a12f4a2011-04-17 18:34:22 -05005109IT(expand);
5110#undef PT
5111#define PT ITSTRUCT(expand)
5112#endif /* PNG_READ_EXPAND_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005113
John Bowler4a12f4a2011-04-17 18:34:22 -05005114#ifdef PNG_READ_EXPAND_SUPPORTED
John Bowlerafea7d12011-01-28 06:38:14 -06005115/* png_set_expand_gray_1_2_4_to_8
5116 * LIBPNG BUG: this just does an 'expand'
5117 */
5118static void
5119image_transform_png_set_expand_gray_1_2_4_to_8_set(
5120 PNG_CONST image_transform *this, transform_display *that, png_structp pp,
5121 png_infop pi)
5122{
5123 png_set_expand_gray_1_2_4_to_8(pp);
5124 this->next->set(this->next, that, pp, pi);
5125}
5126
5127static void
5128image_transform_png_set_expand_gray_1_2_4_to_8_mod(
5129 PNG_CONST image_transform *this, image_pixel *that, png_structp pp,
5130 PNG_CONST transform_display *display)
5131{
5132 image_transform_png_set_expand_mod(this, that, pp, display);
5133}
5134
5135static int
5136image_transform_png_set_expand_gray_1_2_4_to_8_add(image_transform *this,
5137 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
5138{
5139 return image_transform_png_set_expand_add(this, that, colour_type,
5140 bit_depth);
5141}
5142
John Bowler4a12f4a2011-04-17 18:34:22 -05005143IT(expand_gray_1_2_4_to_8);
5144#undef PT
5145#define PT ITSTRUCT(expand_gray_1_2_4_to_8)
5146#endif /* PNG_READ_EXPAND_SUPPORTED */
5147
5148#ifdef PNG_READ_EXPAND_16_SUPPORTED
John Bowler4d562962011-02-12 09:01:20 -06005149/* png_set_expand_16 */
5150static void
5151image_transform_png_set_expand_16_set(PNG_CONST image_transform *this,
5152 transform_display *that, png_structp pp, png_infop pi)
5153{
5154 png_set_expand_16(pp);
5155 this->next->set(this->next, that, pp, pi);
5156}
5157
5158static void
5159image_transform_png_set_expand_16_mod(PNG_CONST image_transform *this,
5160 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5161{
5162 /* Expect expand_16 to expand everything to 16 bits as a result of also
5163 * causing 'expand' to happen.
5164 */
5165 if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05005166 image_pixel_convert_PLTE(that);
John Bowler4d562962011-02-12 09:01:20 -06005167
5168 if (that->have_tRNS)
5169 image_pixel_add_alpha(that, &display->this);
5170
5171 if (that->bit_depth < 16)
5172 that->sample_depth = that->bit_depth = 16;
5173
5174 this->next->mod(this->next, that, pp, display);
5175}
5176
5177static int
5178image_transform_png_set_expand_16_add(image_transform *this,
5179 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
5180{
5181 UNUSED(colour_type)
5182
5183 this->next = *that;
5184 *that = this;
5185
5186 /* expand_16 does something unless the bit depth is already 16. */
5187 return bit_depth < 16;
5188}
5189
John Bowler4a12f4a2011-04-17 18:34:22 -05005190IT(expand_16);
5191#undef PT
5192#define PT ITSTRUCT(expand_16)
5193#endif /* PNG_READ_EXPAND_16_SUPPORTED */
John Bowlerafea7d12011-01-28 06:38:14 -06005194
John Bowler4a12f4a2011-04-17 18:34:22 -05005195#ifdef PNG_READ_16_TO_8_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005196/* png_set_strip_16 */
5197static void
5198image_transform_png_set_strip_16_set(PNG_CONST image_transform *this,
5199 transform_display *that, png_structp pp, png_infop pi)
5200{
5201 png_set_strip_16(pp);
5202 this->next->set(this->next, that, pp, pi);
5203}
5204
5205static void
5206image_transform_png_set_strip_16_mod(PNG_CONST image_transform *this,
5207 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5208{
5209 if (that->bit_depth == 16)
5210 {
John Bowlerafea7d12011-01-28 06:38:14 -06005211 that->sample_depth = that->bit_depth = 8;
John Bowlerf21a0d02011-01-23 23:55:19 -06005212 if (that->red_sBIT > 8) that->red_sBIT = 8;
5213 if (that->green_sBIT > 8) that->green_sBIT = 8;
5214 if (that->blue_sBIT > 8) that->blue_sBIT = 8;
5215 if (that->alpha_sBIT > 8) that->alpha_sBIT = 8;
5216
5217# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
5218 /* The strip 16 algoirithm drops the low 8 bits rather than calculating
5219 * 1/257, so we need to adjust the permitted errors appropriately:
5220 */
5221 {
5222 PNG_CONST double d = (255-128.5)/65535;
5223 that->rede += d;
5224 that->greene += d;
5225 that->bluee += d;
5226 that->alphae += d;
5227 }
5228# endif
5229 }
5230
5231 this->next->mod(this->next, that, pp, display);
5232}
5233
5234static int
5235image_transform_png_set_strip_16_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06005236 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005237{
John Bowlerafea7d12011-01-28 06:38:14 -06005238 UNUSED(colour_type)
John Bowlerf21a0d02011-01-23 23:55:19 -06005239
5240 this->next = *that;
5241 *that = this;
5242
John Bowlerf21a0d02011-01-23 23:55:19 -06005243 return bit_depth > 8;
5244}
5245
John Bowler4a12f4a2011-04-17 18:34:22 -05005246IT(strip_16);
5247#undef PT
5248#define PT ITSTRUCT(strip_16)
5249#endif /* PNG_READ_16_TO_8_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005250
John Bowler4a12f4a2011-04-17 18:34:22 -05005251#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005252/* png_set_strip_alpha */
5253static void
5254image_transform_png_set_strip_alpha_set(PNG_CONST image_transform *this,
5255 transform_display *that, png_structp pp, png_infop pi)
5256{
5257 png_set_strip_alpha(pp);
5258 this->next->set(this->next, that, pp, pi);
5259}
5260
5261static void
5262image_transform_png_set_strip_alpha_mod(PNG_CONST image_transform *this,
5263 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5264{
5265 if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
5266 that->colour_type = PNG_COLOR_TYPE_GRAY;
5267 else if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
5268 that->colour_type = PNG_COLOR_TYPE_RGB;
5269
John Bowlerafea7d12011-01-28 06:38:14 -06005270 that->have_tRNS = 0;
5271 that->alphaf = 1;
John Bowlerafea7d12011-01-28 06:38:14 -06005272
John Bowlerf21a0d02011-01-23 23:55:19 -06005273 this->next->mod(this->next, that, pp, display);
5274}
5275
5276static int
5277image_transform_png_set_strip_alpha_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06005278 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005279{
John Bowlerafea7d12011-01-28 06:38:14 -06005280 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005281
5282 this->next = *that;
5283 *that = this;
5284
John Bowlerf21a0d02011-01-23 23:55:19 -06005285 return (colour_type & PNG_COLOR_MASK_ALPHA) != 0;
5286}
5287
John Bowler4a12f4a2011-04-17 18:34:22 -05005288IT(strip_alpha);
5289#undef PT
5290#define PT ITSTRUCT(strip_alpha)
5291#endif /* PNG_READ_STRIP_ALPHA_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005292
John Bowler4a12f4a2011-04-17 18:34:22 -05005293#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005294/* png_set_rgb_to_gray(png_structp, int err_action, double red, double green)
5295 * png_set_rgb_to_gray_fixed(png_structp, int err_action, png_fixed_point red,
5296 * png_fixed_point green)
5297 * png_get_rgb_to_gray_status
5298 *
5299 * At present the APIs are simply tested using the 16.16 fixed point conversion
5300 * values known to be used inside libpng:
5301 *
5302 * red: 6968
5303 * green: 23434
5304 * blue: 2366
5305 *
5306 * NOTE: this currently ignores the gamma because no gamma is being set, the
5307 * tests on gamma need to happen in the gamma test set.
5308 */
5309static void
5310image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this,
5311 transform_display *that, png_structp pp, png_infop pi)
5312{
5313 PNG_CONST int error_action = 1; /* no error, no defines in png.h */
5314
5315# ifdef PNG_FLOATING_POINT_SUPPORTED
5316 png_set_rgb_to_gray(pp, error_action, -1, -1);
5317# else
5318 png_set_rgb_to_gray_fixed(pp, error_action, -1, -1);
5319# endif
5320
5321 this->next->set(this->next, that, pp, pi);
5322}
5323
5324static void
5325image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
5326 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5327{
5328 if ((that->colour_type & PNG_COLOR_MASK_COLOR) != 0)
5329 {
5330 if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05005331 image_pixel_convert_PLTE(that);
John Bowlerf21a0d02011-01-23 23:55:19 -06005332
5333 /* Image now has RGB channels... */
5334 that->bluef = that->greenf = that->redf = (that->redf * 6968 +
5335 that->greenf * 23434 + that->bluef * 2366) / 32768;
5336 that->bluee = that->greene = that->rede = (that->rede * 6968 +
5337 that->greene * 23434 + that->bluee * 2366) / 32768 *
5338 (1 + DBL_EPSILON * 6);
5339
5340 /* The sBIT is the minium of the three colour channel sBITs. */
5341 if (that->red_sBIT > that->green_sBIT)
5342 that->red_sBIT = that->green_sBIT;
5343 if (that->red_sBIT > that->blue_sBIT)
5344 that->red_sBIT = that->blue_sBIT;
5345 that->blue_sBIT = that->green_sBIT = that->red_sBIT;
5346
5347 /* And zap the colour bit in the type: */
5348 if (that->colour_type == PNG_COLOR_TYPE_RGB)
5349 that->colour_type = PNG_COLOR_TYPE_GRAY;
5350 else if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
5351 that->colour_type = PNG_COLOR_TYPE_GRAY_ALPHA;
5352 }
5353
5354 this->next->mod(this->next, that, pp, display);
5355}
5356
5357static int
5358image_transform_png_set_rgb_to_gray_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06005359 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005360{
John Bowlerafea7d12011-01-28 06:38:14 -06005361 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005362
5363 this->next = *that;
5364 *that = this;
5365
John Bowlerf21a0d02011-01-23 23:55:19 -06005366 return (colour_type & PNG_COLOR_MASK_COLOR) != 0;
5367}
5368
John Bowler4a12f4a2011-04-17 18:34:22 -05005369IT(rgb_to_gray);
5370#undef PT
5371#define PT ITSTRUCT(rgb_to_gray)
5372#endif /* PNG_READ_RGB_TO_GRAY_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005373
John Bowler4a12f4a2011-04-17 18:34:22 -05005374#ifdef PNG_READ_BACKGROUND_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005375/* png_set_background(png_structp, png_const_color_16p background_color,
5376 * int background_gamma_code, int need_expand, double background_gamma)
5377 * png_set_background_fixed(png_structp, png_const_color_16p background_color,
5378 * int background_gamma_code, int need_expand,
5379 * png_fixed_point background_gamma)
5380 *
5381 * As with rgb_to_gray this ignores the gamma.
5382*/
5383static void
5384image_transform_png_set_background_set(PNG_CONST image_transform *this,
5385 transform_display *that, png_structp pp, png_infop pi)
5386{
John Bowler9994f252011-05-15 18:52:39 -05005387 png_byte colour_type, bit_depth;
5388 png_byte random_bytes[8]; /* 8 bytes - 64 bits - the biggest pixel */
John Bowlerf21a0d02011-01-23 23:55:19 -06005389 png_color_16 back;
5390
John Bowler9994f252011-05-15 18:52:39 -05005391 /* We need a background colour, because we don't know exactly what transforms
5392 * have been set we have to supply the colour in the original file format and
5393 * so we need to know what that is! The background colour is stored in the
5394 * transform_display.
John Bowlerafea7d12011-01-28 06:38:14 -06005395 */
John Bowler9994f252011-05-15 18:52:39 -05005396 RANDOMIZE(random_bytes);
John Bowlerf21a0d02011-01-23 23:55:19 -06005397
John Bowler9994f252011-05-15 18:52:39 -05005398 /* Read the random value, for colour type 3 the background colour is actually
5399 * expressed as a 24bit rgb, not an index.
5400 */
5401 colour_type = that->this.colour_type;
5402 if (colour_type == 3)
5403 {
5404 colour_type = PNG_COLOR_TYPE_RGB;
5405 bit_depth = 8;
5406 }
5407
5408 else
5409 bit_depth = that->this.bit_depth;
5410
5411 image_pixel_init(&that->background_colour, random_bytes, colour_type,
5412 bit_depth, 0/*x*/, 0/*unused: palette*/);
5413
5414 /* Extract the background colour from this image_pixel, but make sure the
5415 * unused fields of 'back' are garbage.
5416 */
5417 RANDOMIZE(back);
5418
5419 if (colour_type & PNG_COLOR_MASK_COLOR)
5420 {
5421 back.red = (png_uint_16)that->background_colour.red;
5422 back.green = (png_uint_16)that->background_colour.green;
5423 back.blue = (png_uint_16)that->background_colour.blue;
5424 }
5425
5426 else
5427 back.gray = (png_uint_16)that->background_colour.red;
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -05005428
John Bowlerf21a0d02011-01-23 23:55:19 -06005429# ifdef PNG_FLOATING_POINT_SUPPORTED
John Bowler9994f252011-05-15 18:52:39 -05005430 png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, 1/*need expand*/,
5431 0);
John Bowlerf21a0d02011-01-23 23:55:19 -06005432# else
John Bowler9994f252011-05-15 18:52:39 -05005433 png_set_background_fixed(pp, &back, PNG_BACKGROUND_GAMMA_FILE,
5434 1/*need expand*/, 0);
John Bowlerf21a0d02011-01-23 23:55:19 -06005435# endif
5436
5437 this->next->set(this->next, that, pp, pi);
5438}
5439
5440static void
5441image_transform_png_set_background_mod(PNG_CONST image_transform *this,
5442 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5443{
John Bowlerafea7d12011-01-28 06:38:14 -06005444 /* Check for tRNS first: */
5445 if (that->have_tRNS && that->colour_type != PNG_COLOR_TYPE_PALETTE)
John Bowlerf21a0d02011-01-23 23:55:19 -06005446 image_pixel_add_alpha(that, &display->this);
5447
John Bowlerafea7d12011-01-28 06:38:14 -06005448 /* This is only necessary if the alpha value is less than 1. */
5449 if (that->alphaf < 1)
John Bowlerf21a0d02011-01-23 23:55:19 -06005450 {
John Bowler9994f252011-05-15 18:52:39 -05005451 PNG_CONST image_pixel *back = &display->background_colour;
John Bowlerf21a0d02011-01-23 23:55:19 -06005452
John Bowlerafea7d12011-01-28 06:38:14 -06005453 /* Now we do the background calculation without any gamma correction. */
John Bowlerf21a0d02011-01-23 23:55:19 -06005454 if (that->alphaf <= 0)
5455 {
John Bowler9994f252011-05-15 18:52:39 -05005456 that->redf = back->redf;
5457 that->greenf = back->greenf;
5458 that->bluef = back->bluef;
5459
5460 that->rede = back->rede;
5461 that->greene = back->greene;
5462 that->bluee = back->bluee;
5463
5464 that->red_sBIT= back->red_sBIT;
5465 that->green_sBIT= back->green_sBIT;
5466 that->blue_sBIT= back->blue_sBIT;
John Bowlerf21a0d02011-01-23 23:55:19 -06005467 }
5468
John Bowler9994f252011-05-15 18:52:39 -05005469 else /* 0 < alpha < 1 */
John Bowlerf21a0d02011-01-23 23:55:19 -06005470 {
John Bowler9994f252011-05-15 18:52:39 -05005471 double alf = 1 - that->alphaf;
5472
5473 that->redf = that->redf * that->alphaf + back->redf * alf;
5474 that->rede = that->rede * that->alphaf + back->rede * alf +
John Bowlerf21a0d02011-01-23 23:55:19 -06005475 DBL_EPSILON;
John Bowler9994f252011-05-15 18:52:39 -05005476 that->greenf = that->greenf * that->alphaf + back->greenf * alf;
5477 that->greene = that->greene * that->alphaf + back->greene * alf +
5478 DBL_EPSILON;
5479 that->bluef = that->bluef * that->alphaf + back->bluef * alf;
5480 that->bluee = that->bluee * that->alphaf + back->bluee * alf +
John Bowlerf21a0d02011-01-23 23:55:19 -06005481 DBL_EPSILON;
5482 }
5483
John Bowler9994f252011-05-15 18:52:39 -05005484 /* Remove the alpha type and set the alpha (not in that order.) */
John Bowlerafea7d12011-01-28 06:38:14 -06005485 that->alphaf = 1;
5486 that->alphae = 0;
John Bowlerf21a0d02011-01-23 23:55:19 -06005487
John Bowlerafea7d12011-01-28 06:38:14 -06005488 if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
5489 that->colour_type = PNG_COLOR_TYPE_RGB;
5490 else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
5491 that->colour_type = PNG_COLOR_TYPE_GRAY;
John Bowler9994f252011-05-15 18:52:39 -05005492 /* PNG_COLOR_TYPE_PALETTE is not changed */
John Bowlerf21a0d02011-01-23 23:55:19 -06005493 }
5494
5495 this->next->mod(this->next, that, pp, display);
5496}
5497
John Bowlerafea7d12011-01-28 06:38:14 -06005498#define image_transform_png_set_background_add image_transform_default_add
John Bowlerf21a0d02011-01-23 23:55:19 -06005499
John Bowler4a12f4a2011-04-17 18:34:22 -05005500IT(background);
5501#undef PT
5502#define PT ITSTRUCT(background)
5503#endif /* PNG_READ_BACKGROUND_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005504
John Bowler4a12f4a2011-04-17 18:34:22 -05005505/* This may just be 'end' if all the transforms are disabled! */
5506static image_transform *PNG_CONST image_transform_first = &PT;
John Bowlerf21a0d02011-01-23 23:55:19 -06005507
John Bowlerafea7d12011-01-28 06:38:14 -06005508static void
5509transform_enable(PNG_CONST char *name)
John Bowlerf21a0d02011-01-23 23:55:19 -06005510{
John Bowlerafea7d12011-01-28 06:38:14 -06005511 /* Everything starts out enabled, so if we see an 'enable' disabled
5512 * everything else the first time round.
5513 */
5514 static int all_disabled = 0;
5515 int found_it = 0;
5516 image_transform *list = image_transform_first;
5517
5518 while (list != &image_transform_end)
5519 {
5520 if (strcmp(list->name, name) == 0)
5521 {
5522 list->enable = 1;
5523 found_it = 1;
5524 }
5525 else if (!all_disabled)
5526 list->enable = 0;
5527
5528 list = list->list;
5529 }
5530
5531 all_disabled = 1;
5532
5533 if (!found_it)
5534 {
5535 fprintf(stderr, "pngvalid: --transform-enable=%s: unknown transform\n",
5536 name);
5537 exit(1);
5538 }
5539}
5540
5541static void
5542transform_disable(PNG_CONST char *name)
5543{
5544 image_transform *list = image_transform_first;
5545
5546 while (list != &image_transform_end)
5547 {
5548 if (strcmp(list->name, name) == 0)
5549 {
5550 list->enable = 0;
5551 return;
5552 }
5553
5554 list = list->list;
5555 }
5556
5557 fprintf(stderr, "pngvalid: --transform-disable=%s: unknown transform\n",
5558 name);
5559 exit(1);
5560}
5561
5562static void
5563image_transform_reset_count(void)
5564{
5565 image_transform *next = image_transform_first;
5566 int count = 0;
5567
5568 while (next != &image_transform_end)
5569 {
5570 next->local_use = 0;
5571 next->next = 0;
5572 next = next->list;
5573 ++count;
5574 }
5575
5576 /* This can only happen if we every have more than 32 transforms (excluding
5577 * the end) in the list.
5578 */
5579 if (count > 32) abort();
5580}
5581
5582static int
5583image_transform_test_counter(png_uint_32 counter, unsigned int max)
5584{
5585 /* Test the list to see if there is any point contining, given a current
5586 * counter and a 'max' value.
5587 */
5588 image_transform *next = image_transform_first;
5589
5590 while (next != &image_transform_end)
5591 {
5592 /* For max 0 or 1 continue until the counter overflows: */
5593 counter >>= 1;
5594
5595 /* Continue if any entry hasn't reacked the max. */
5596 if (max > 1 && next->local_use < max)
5597 return 1;
5598 next = next->list;
5599 }
5600
5601 return max <= 1 && counter == 0;
5602}
John Bowlerf21a0d02011-01-23 23:55:19 -06005603
5604static png_uint_32
John Bowlerafea7d12011-01-28 06:38:14 -06005605image_transform_add(PNG_CONST image_transform **this, unsigned int max,
John Bowlerf21a0d02011-01-23 23:55:19 -06005606 png_uint_32 counter, char *name, size_t sizeof_name, size_t *pos,
5607 png_byte colour_type, png_byte bit_depth)
5608{
John Bowlerafea7d12011-01-28 06:38:14 -06005609 for (;;) /* until we manage to add something */
John Bowlerf21a0d02011-01-23 23:55:19 -06005610 {
John Bowlerafea7d12011-01-28 06:38:14 -06005611 png_uint_32 mask;
5612 image_transform *list;
John Bowlerf21a0d02011-01-23 23:55:19 -06005613
John Bowlerafea7d12011-01-28 06:38:14 -06005614 /* Find the next counter value, if the counter is zero this is the start
5615 * of the list. This routine always returns the current counter (not the
5616 * next) so it returns 0 at the end and expects 0 at the beginning.
5617 */
5618 if (counter == 0) /* first time */
John Bowlerf21a0d02011-01-23 23:55:19 -06005619 {
John Bowlerafea7d12011-01-28 06:38:14 -06005620 image_transform_reset_count();
5621 if (max <= 1)
5622 counter = 1;
5623 else
5624 counter = random_32();
5625 }
5626 else /* advance the counter */
5627 {
5628 switch (max)
John Bowlerf21a0d02011-01-23 23:55:19 -06005629 {
John Bowlerafea7d12011-01-28 06:38:14 -06005630 case 0: ++counter; break;
5631 case 1: counter <<= 1; break;
5632 default: counter = random_32(); break;
John Bowlerf21a0d02011-01-23 23:55:19 -06005633 }
5634 }
5635
John Bowlerafea7d12011-01-28 06:38:14 -06005636 /* Now add all these items, if possible */
5637 *this = &image_transform_end;
5638 list = image_transform_first;
5639 mask = 1;
John Bowlerf21a0d02011-01-23 23:55:19 -06005640
John Bowlerafea7d12011-01-28 06:38:14 -06005641 /* Go through the whole list adding anything that the counter selects: */
5642 while (list != &image_transform_end)
5643 {
5644 if ((counter & mask) != 0 && list->enable &&
5645 (max == 0 || list->local_use < max))
5646 {
5647 /* Candidate to add: */
5648 if (list->add(list, this, colour_type, bit_depth) || max == 0)
5649 {
5650 /* Added, so add to the name too. */
5651 *pos = safecat(name, sizeof_name, *pos, " +");
5652 *pos = safecat(name, sizeof_name, *pos, list->name);
5653 }
John Bowlerf21a0d02011-01-23 23:55:19 -06005654
John Bowlerafea7d12011-01-28 06:38:14 -06005655 else
5656 {
5657 /* Not useful and max>0, so remvoe it from *this: */
5658 *this = list->next;
5659 list->next = 0;
John Bowlerf21a0d02011-01-23 23:55:19 -06005660
John Bowlerafea7d12011-01-28 06:38:14 -06005661 /* And, since we know it isn't useful, stop it being added again
5662 * in this run:
5663 */
5664 list->local_use = max;
5665 }
5666 }
John Bowlerf21a0d02011-01-23 23:55:19 -06005667
John Bowlerafea7d12011-01-28 06:38:14 -06005668 mask <<= 1;
5669 list = list->list;
5670 }
John Bowlerf21a0d02011-01-23 23:55:19 -06005671
John Bowlerafea7d12011-01-28 06:38:14 -06005672 /* Now if anything was added we have something to do. */
5673 if (*this != &image_transform_end)
5674 return counter;
5675
5676 /* Nothing added, but was there anything in there to add? */
5677 if (!image_transform_test_counter(counter, max))
5678 return 0;
John Bowlerf21a0d02011-01-23 23:55:19 -06005679 }
5680}
5681
5682#ifdef THIS_IS_THE_PROFORMA
5683static void
5684image_transform_png_set_@_set(PNG_CONST image_transform *this,
5685 transform_display *that, png_structp pp, png_infop pi)
5686{
5687 png_set_@(pp);
5688 this->next->set(this->next, that, pp, pi);
5689}
5690
5691static void
5692image_transform_png_set_@_mod(PNG_CONST image_transform *this,
5693 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5694{
5695 this->next->mod(this->next, that, pp, display);
5696}
5697
5698static int
5699image_transform_png_set_@_add(image_transform *this,
5700 PNG_CONST image_transform **that, char *name, size_t sizeof_name,
5701 size_t *pos, png_byte colour_type, png_byte bit_depth)
5702{
5703 this->next = *that;
5704 *that = this;
5705
5706 *pos = safecat(name, sizeof_name, *pos, " +@");
5707
5708 return 1;
5709}
5710
5711IT(@);
5712#endif
5713
5714/* png_set_quantize(png_structp, png_colorp palette, int num_palette,
5715 * int maximum_colors, png_const_uint_16p histogram, int full_quantize)
5716 *
5717 * Very difficult to validate this!
5718 */
5719/*NOTE: TBD NYI */
5720
5721/* The data layout transforms are handled by swapping our own channel data,
5722 * necessarily these need to happen at the end of the transform list because the
5723 * semantic of the channels changes after these are executed. Some of these,
5724 * like set_shift and set_packing, can't be done at present because they change
5725 * the layout of the data at the sub-sample level so sample() won't get the
5726 * right answer.
5727 */
5728/* png_set_invert_alpha */
5729/*NOTE: TBD NYI */
5730
5731/* png_set_bgr */
5732/*NOTE: TBD NYI */
5733
5734/* png_set_swap_alpha */
5735/*NOTE: TBD NYI */
5736
5737/* png_set_swap */
5738/*NOTE: TBD NYI */
5739
5740/* png_set_filler, (png_structp png_ptr, png_uint_32 filler, int flags)); */
5741/*NOTE: TBD NYI */
5742
5743/* png_set_add_alpha, (png_structp png_ptr, png_uint_32 filler, int flags)); */
5744/*NOTE: TBD NYI */
5745
5746/* png_set_packing */
5747/*NOTE: TBD NYI */
5748
5749/* png_set_packswap */
5750/*NOTE: TBD NYI */
5751
5752/* png_set_invert_mono */
5753/*NOTE: TBD NYI */
5754
5755/* png_set_shift(png_structp, png_const_color_8p true_bits) */
5756/*NOTE: TBD NYI */
5757
John Bowler9994f252011-05-15 18:52:39 -05005758static void
5759perform_transform_test(png_modifier *pm)
John Bowlerf21a0d02011-01-23 23:55:19 -06005760{
John Bowler9994f252011-05-15 18:52:39 -05005761 png_byte colour_type = 0;
5762 png_byte bit_depth = 0;
5763 int palette_number = 0;
5764
5765 while (next_format(&colour_type, &bit_depth, &palette_number))
John Bowlerf21a0d02011-01-23 23:55:19 -06005766 {
John Bowlerafea7d12011-01-28 06:38:14 -06005767 png_uint_32 counter = 0;
John Bowlerf21a0d02011-01-23 23:55:19 -06005768 size_t base_pos;
5769 char name[64];
5770
5771 base_pos = safecat(name, sizeof name, 0, "transform:");
5772
5773 for (;;)
5774 {
5775 size_t pos = base_pos;
5776 PNG_CONST image_transform *list = 0;
5777
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05005778 /* 'max' is currently hardwired to '1'; this should be settable on the
John Bowler9994f252011-05-15 18:52:39 -05005779 * command line.
5780 */
5781 counter = image_transform_add(&list, 1/*max*/, counter,
5782 name, sizeof name, &pos, colour_type, bit_depth);
John Bowlerf21a0d02011-01-23 23:55:19 -06005783
5784 if (counter == 0)
5785 break;
5786
5787 /* The command line can change this to checking interlaced images. */
John Bowler9994f252011-05-15 18:52:39 -05005788 transform_test(pm, FILEID(colour_type, bit_depth, palette_number,
5789 pm->interlace_type, 0, 0, 0), list, name);
John Bowlerf21a0d02011-01-23 23:55:19 -06005790
5791 if (fail(pm))
John Bowler9994f252011-05-15 18:52:39 -05005792 return;
John Bowlerf21a0d02011-01-23 23:55:19 -06005793 }
5794 }
John Bowlerf21a0d02011-01-23 23:55:19 -06005795}
John Bowler4a12f4a2011-04-17 18:34:22 -05005796#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005797
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005798/********************************* GAMMA TESTS ********************************/
John Bowler4a12f4a2011-04-17 18:34:22 -05005799#ifdef PNG_READ_GAMMA_SUPPORTED
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005800/* Gamma test images. */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005801typedef struct gamma_modification
5802{
5803 png_modification this;
5804 png_fixed_point gamma;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005805} gamma_modification;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005806
5807static int
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005808gamma_modify(png_modifier *pm, png_modification *me, int add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005809{
John Bowlerafea7d12011-01-28 06:38:14 -06005810 UNUSED(add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005811 /* This simply dumps the given gamma value into the buffer. */
5812 png_save_uint_32(pm->buffer, 4);
5813 png_save_uint_32(pm->buffer+4, CHUNK_gAMA);
5814 png_save_uint_32(pm->buffer+8, ((gamma_modification*)me)->gamma);
5815 return 1;
5816}
5817
5818static void
John Bowler168a4332011-01-16 19:32:22 -06005819gamma_modification_init(gamma_modification *me, png_modifier *pm, double gammad)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005820{
Glenn Randers-Pehrson21b4b332010-08-18 07:12:38 -05005821 double g;
5822
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005823 modification_init(&me->this);
5824 me->this.chunk = CHUNK_gAMA;
5825 me->this.modify_fn = gamma_modify;
5826 me->this.add = CHUNK_PLTE;
John Bowler168a4332011-01-16 19:32:22 -06005827 g = floor(gammad * 100000 + .5);
Glenn Randers-Pehrson21b4b332010-08-18 07:12:38 -05005828 me->gamma = (png_fixed_point)g;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005829 me->this.next = pm->modifications;
5830 pm->modifications = &me->this;
5831}
5832
5833typedef struct srgb_modification
5834{
5835 png_modification this;
5836 png_byte intent;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005837} srgb_modification;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005838
5839static int
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005840srgb_modify(png_modifier *pm, png_modification *me, int add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005841{
John Bowlerafea7d12011-01-28 06:38:14 -06005842 UNUSED(add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005843 /* As above, ignore add and just make a new chunk */
5844 png_save_uint_32(pm->buffer, 1);
5845 png_save_uint_32(pm->buffer+4, CHUNK_sRGB);
5846 pm->buffer[8] = ((srgb_modification*)me)->intent;
5847 return 1;
5848}
5849
5850static void
5851srgb_modification_init(srgb_modification *me, png_modifier *pm, png_byte intent)
5852{
5853 modification_init(&me->this);
5854 me->this.chunk = CHUNK_sBIT;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005855
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005856 if (intent <= 3) /* if valid, else *delete* sRGB chunks */
5857 {
5858 me->this.modify_fn = srgb_modify;
5859 me->this.add = CHUNK_PLTE;
5860 me->intent = intent;
5861 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005862
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005863 else
5864 {
5865 me->this.modify_fn = 0;
5866 me->this.add = 0;
5867 me->intent = 0;
5868 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005869
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005870 me->this.next = pm->modifications;
5871 pm->modifications = &me->this;
5872}
5873
5874typedef struct sbit_modification
5875{
5876 png_modification this;
5877 png_byte sbit;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005878} sbit_modification;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005879
5880static int
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005881sbit_modify(png_modifier *pm, png_modification *me, int add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005882{
5883 png_byte sbit = ((sbit_modification*)me)->sbit;
5884 if (pm->bit_depth > sbit)
5885 {
5886 int cb = 0;
5887 switch (pm->colour_type)
5888 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005889 case 0:
5890 cb = 1;
5891 break;
5892
5893 case 2:
5894 case 3:
5895 cb = 3;
5896 break;
5897
5898 case 4:
5899 cb = 2;
5900 break;
5901
5902 case 6:
5903 cb = 4;
5904 break;
5905
5906 default:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005907 png_error(pm->this.pread,
5908 "unexpected colour type in sBIT modification");
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005909 }
5910
5911 png_save_uint_32(pm->buffer, cb);
5912 png_save_uint_32(pm->buffer+4, CHUNK_sBIT);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005913
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005914 while (cb > 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05005915 (pm->buffer+8)[--cb] = sbit;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005916
5917 return 1;
5918 }
5919 else if (!add)
5920 {
5921 /* Remove the sBIT chunk */
5922 pm->buffer_count = pm->buffer_position = 0;
5923 return 1;
5924 }
5925 else
5926 return 0; /* do nothing */
5927}
5928
5929static void
5930sbit_modification_init(sbit_modification *me, png_modifier *pm, png_byte sbit)
5931{
5932 modification_init(&me->this);
5933 me->this.chunk = CHUNK_sBIT;
5934 me->this.modify_fn = sbit_modify;
5935 me->this.add = CHUNK_PLTE;
5936 me->sbit = sbit;
5937 me->this.next = pm->modifications;
5938 pm->modifications = &me->this;
5939}
5940
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005941/* Reader callbacks and implementations, where they differ from the standard
5942 * ones.
5943 */
5944typedef struct gamma_display
5945{
5946 standard_display this;
5947
5948 /* Parameters */
5949 png_modifier* pm;
5950 double file_gamma;
5951 double screen_gamma;
John Bowlerd273ad22011-05-07 21:00:28 -05005952 double background_gamma;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005953 png_byte sbit;
5954 int threshold_test;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005955 int use_input_precision;
5956 int strip16;
John Bowlerd273ad22011-05-07 21:00:28 -05005957 int expand16;
5958 int do_background;
5959 png_color_16 background_color;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005960
5961 /* Local variables */
5962 double maxerrout;
5963 double maxerrpc;
5964 double maxerrabs;
5965} gamma_display;
5966
John Bowlerd273ad22011-05-07 21:00:28 -05005967#define ALPHA_MODE_OFFSET 4
5968
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005969static void
John Bowler660c6e42010-12-19 06:22:23 -06005970gamma_display_init(gamma_display *dp, png_modifier *pm, png_uint_32 id,
5971 double file_gamma, double screen_gamma, png_byte sbit, int threshold_test,
John Bowler9994f252011-05-15 18:52:39 -05005972 int use_input_precision, int strip16, int expand16,
John Bowlerd273ad22011-05-07 21:00:28 -05005973 int do_background, PNG_CONST png_color_16 *pointer_to_the_background_color,
5974 double background_gamma)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005975{
5976 /* Standard fields */
John Bowler660c6e42010-12-19 06:22:23 -06005977 standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06005978
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005979 /* Parameter fields */
5980 dp->pm = pm;
5981 dp->file_gamma = file_gamma;
5982 dp->screen_gamma = screen_gamma;
John Bowlerd273ad22011-05-07 21:00:28 -05005983 dp->background_gamma = background_gamma;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005984 dp->sbit = sbit;
5985 dp->threshold_test = threshold_test;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005986 dp->use_input_precision = use_input_precision;
5987 dp->strip16 = strip16;
John Bowlerd273ad22011-05-07 21:00:28 -05005988 dp->expand16 = expand16;
5989 dp->do_background = do_background;
5990 if (do_background && pointer_to_the_background_color != 0)
5991 dp->background_color = *pointer_to_the_background_color;
5992 else
5993 memset(&dp->background_color, 0, sizeof dp->background_color);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005994
5995 /* Local variable fields */
5996 dp->maxerrout = dp->maxerrpc = dp->maxerrabs = 0;
5997}
5998
5999static void
6000gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi)
6001{
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05006002 /* Reuse the standard stuff as appropriate. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006003 standard_info_part1(&dp->this, pp, pi);
6004
6005 /* If requested strip 16 to 8 bits - this is handled automagically below
6006 * because the output bit depth is read from the library. Note that there
6007 * are interactions with sBIT but, internally, libpng makes sbit at most
6008 * PNG_MAX_GAMMA_8 when doing the following.
6009 */
6010 if (dp->strip16)
John Bowlerb54498e2010-12-08 16:26:21 -06006011# ifdef PNG_READ_16_TO_8_SUPPORTED
Glenn Randers-Pehrsonfded04f2010-08-27 14:21:21 -05006012 png_set_strip_16(pp);
6013# else
6014 png_error(pp, "strip16 (16 to 8 bit conversion) not supported");
6015# endif
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006016
John Bowlerd273ad22011-05-07 21:00:28 -05006017 if (dp->expand16)
6018# ifdef PNG_READ_EXPAND_16_SUPPORTED
6019 png_set_expand_16(pp);
6020# else
6021 png_error(pp, "expand16 (8 to 16 bit conversion) not supported");
6022# endif
6023
6024 if (dp->do_background >= ALPHA_MODE_OFFSET)
6025 {
6026# ifdef PNG_READ_ALPHA_MODE_SUPPORTED
6027 {
6028 /* This tests the alpha mode handling, if supported. */
6029 int mode = dp->do_background - ALPHA_MODE_OFFSET;
6030
6031 /* The gamma value is the output gamma, and is in the standard,
6032 * non-inverted, represenation. It provides a default for the PNG file
6033 * gamma, but since the file has a gAMA chunk this does not matter.
6034 */
6035 PNG_CONST double sg = dp->screen_gamma;
6036# ifndef PNG_FLOATING_POINT_SUPPORTED
6037 PNG_CONST png_fixed_point g = (png_fixed_point)(sg*100000+.5);
6038# endif
6039
6040# ifdef PNG_FLOATING_POINT_SUPPORTED
6041 png_set_alpha_mode(pp, mode, sg);
6042# else
6043 png_set_alpha_mode_fixed(pp, mode, g);
6044# endif
6045
6046 /* However, for the standard Porter-Duff algorithm the output defaults
6047 * to be linear, so if the test requires non-linear output it must be
6048 * corrected here.
6049 */
6050 if (mode == PNG_ALPHA_STANDARD && sg != 1)
6051 {
6052# ifdef PNG_FLOATING_POINT_SUPPORTED
6053 png_set_gamma(pp, sg, dp->file_gamma);
6054# else
6055 png_fixed_point f = (png_fixed_point)(dp->file_gamma*100000+.5);
6056 png_set_gamma_fixed(pp, g, f);
6057# endif
6058 }
6059 }
6060# else
6061 png_error(pp, "alpha mode handling not supported");
6062# endif
6063 }
6064
6065 else
6066 {
6067 /* Set up gamma processing. */
6068# ifdef PNG_FLOATING_POINT_SUPPORTED
6069 png_set_gamma(pp, dp->screen_gamma, dp->file_gamma);
6070# else
6071 {
6072 png_fixed_point s = (png_fixed_point)(dp->screen_gamma*100000+.5);
6073 png_fixed_point f = (png_fixed_point)(dp->file_gamma*100000+.5);
6074 png_set_gamma_fixed(pp, s, f);
6075 }
6076# endif
6077
6078 if (dp->do_background)
6079 {
6080# ifdef PNG_READ_BACKGROUND_SUPPORTED
6081 /* NOTE: this assumes the caller provided the correct background gamma!
6082 */
6083 PNG_CONST double bg = dp->background_gamma;
6084# ifndef PNG_FLOATING_POINT_SUPPORTED
6085 PNG_CONST png_fixed_point g = (png_fixed_point)(bg*100000+.5);
6086# endif
6087
6088# ifdef PNG_FLOATING_POINT_SUPPORTED
6089 png_set_background(pp, &dp->background_color, dp->do_background,
6090 0/*need_expand*/, bg);
6091# else
6092 png_set_background_fixed(pp, &dp->background_color,
6093 dp->do_background, 0/*need_expand*/, g);
6094# endif
6095# else
6096 png_error(pp, "png_set_background not supported");
6097# endif
6098 }
6099 }
6100
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006101 png_read_update_info(pp, pi);
6102
6103 /* Now we may get a different cbRow: */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006104 standard_info_part2(&dp->this, pp, pi, 1 /*images*/);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006105}
6106
6107static void
6108gamma_info(png_structp pp, png_infop pi)
6109{
6110 gamma_info_imp(png_get_progressive_ptr(pp), pp, pi);
6111}
6112
John Bowlerd273ad22011-05-07 21:00:28 -05006113/* Validate a single component value - the routine gets the input and output
6114 * sample values as unscaled PNG component values along with a cache of all the
6115 * information required to validate the values.
6116 */
6117typedef struct validate_info
6118{
6119 png_structp pp;
6120 gamma_display *dp;
6121 png_byte sbit;
6122 int use_input_precision;
6123 int do_background;
6124 int strip16;
6125 unsigned int sbit_max;
6126 unsigned int isbit_shift;
6127 unsigned int outmax;
6128
6129 double gamma_correction; /* Overall correction required. */
6130 double file_inverse; /* Inverse of file gamma. */
6131 double screen_gamma;
6132 double screen_inverse; /* Inverse of screen gamma. */
6133
6134 double background_red; /* Linear background value, red or gray. */
6135 double background_green;
6136 double background_blue;
6137
6138 double maxabs;
John Bowler5441e182011-05-18 18:57:12 -05006139 double maxpc;
John Bowlerd273ad22011-05-07 21:00:28 -05006140 double maxcalc;
6141 double maxout;
John Bowler5441e182011-05-18 18:57:12 -05006142 double maxout_total; /* Total including quantization error */
6143 int outquant;
John Bowlerd273ad22011-05-07 21:00:28 -05006144}
6145validate_info;
6146
6147static void
6148init_validate_info(validate_info *vi, gamma_display *dp, png_struct *pp,
John Bowler5441e182011-05-18 18:57:12 -05006149 int in_depth, int out_depth)
John Bowlerd273ad22011-05-07 21:00:28 -05006150{
John Bowler5441e182011-05-18 18:57:12 -05006151 PNG_CONST unsigned int outmax = (1U<<out_depth)-1;
John Bowlerd273ad22011-05-07 21:00:28 -05006152
6153 vi->pp = pp;
6154 vi->dp = dp;
John Bowler5441e182011-05-18 18:57:12 -05006155
6156 if (dp->sbit > 0 && dp->sbit < in_depth)
6157 {
6158 vi->sbit = dp->sbit;
6159 vi->isbit_shift = in_depth - dp->sbit;
6160 }
6161
6162 else
6163 {
6164 vi->sbit = (png_byte)in_depth;
6165 vi->isbit_shift = 0;
6166 }
6167
6168 vi->sbit_max = (1U << vi->sbit)-1;
John Bowlerd273ad22011-05-07 21:00:28 -05006169
6170 /* This mimics the libpng threshold test, '0' is used to prevent gamma
6171 * correction in the validation test.
6172 */
6173 vi->screen_gamma = dp->screen_gamma;
6174 if (fabs(vi->screen_gamma-1) < PNG_GAMMA_THRESHOLD)
6175 vi->screen_gamma = vi->screen_inverse = 0;
6176 else
6177 vi->screen_inverse = 1/vi->screen_gamma;
6178
6179 vi->use_input_precision = dp->use_input_precision;
6180 vi->outmax = outmax;
John Bowler5441e182011-05-18 18:57:12 -05006181 vi->maxabs = abserr(dp->pm, in_depth, out_depth);
6182 vi->maxpc = pcerr(dp->pm, in_depth, out_depth);
6183 vi->maxcalc = calcerr(dp->pm, in_depth, out_depth);
6184 vi->maxout = outerr(dp->pm, in_depth, out_depth);
6185 vi->outquant = output_quantization_factor(dp->pm, in_depth, out_depth);
6186 vi->maxout_total = vi->maxout + vi->outquant * .5;
John Bowlerd273ad22011-05-07 21:00:28 -05006187
John Bowler5441e182011-05-18 18:57:12 -05006188 if ((dp->this.colour_type & PNG_COLOR_MASK_ALPHA) != 0 ||
6189 (dp->this.colour_type == 3 && dp->this.is_transparent))
John Bowlerd273ad22011-05-07 21:00:28 -05006190 {
6191 vi->do_background = dp->do_background;
6192
6193 if (vi->do_background != 0)
6194 {
6195 PNG_CONST double bg_inverse = 1/dp->background_gamma;
6196 double r, g, b;
6197
6198 /* Caller must at least put the gray value into the red channel */
6199 r = dp->background_color.red; r /= outmax;
6200 g = dp->background_color.green; g /= outmax;
6201 b = dp->background_color.blue; b /= outmax;
6202
6203# if 0
6204 /* libpng doesn't do this optimization, if we do pngvalid will fail.
6205 */
6206 if (fabs(bg_inverse-1) >= PNG_GAMMA_THRESHOLD)
6207# endif
6208 {
6209 r = pow(r, bg_inverse);
6210 g = pow(g, bg_inverse);
6211 b = pow(b, bg_inverse);
6212 }
6213
6214 vi->background_red = r;
6215 vi->background_green = g;
6216 vi->background_blue = b;
6217 }
6218 }
6219 else
6220 vi->do_background = 0;
6221
6222 if (vi->do_background == 0)
6223 vi->background_red = vi->background_green = vi->background_blue = 0;
6224
6225 vi->gamma_correction = 1/(dp->file_gamma*dp->screen_gamma);
6226 if (fabs(vi->gamma_correction-1) < PNG_GAMMA_THRESHOLD)
6227 vi->gamma_correction = 0;
6228
6229 vi->file_inverse = 1/dp->file_gamma;
6230 if (fabs(vi->file_inverse-1) < PNG_GAMMA_THRESHOLD)
6231 vi->file_inverse = 0;
6232
6233 vi->strip16 = dp->strip16;
6234}
6235
John Bowler5441e182011-05-18 18:57:12 -05006236/* This function handles composition of a single non-alpha component. The
6237 * argument is the input sample value, in the range 0..1, and the alpha value.
6238 * The result is the composed, linear, input sample. If alpha is less than zero
6239 * this is the alpha component and the function should not be called!
6240 */
6241static double
6242gamma_component_compose(int do_background, double input_sample, double alpha,
6243 double background, int *compose)
6244{
6245 switch (do_background)
6246 {
6247 case PNG_BACKGROUND_GAMMA_SCREEN:
6248 case PNG_BACKGROUND_GAMMA_FILE:
6249 case PNG_BACKGROUND_GAMMA_UNIQUE:
6250 /* Standard PNG background processing. */
6251 if (alpha < 1)
6252 {
6253 if (alpha > 0)
6254 {
6255 input_sample = input_sample * alpha + background * (1-alpha);
6256 if (compose != NULL)
6257 *compose = 1;
6258 }
6259
6260 else
6261 input_sample = background;
6262 }
6263 break;
6264
6265#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
6266 case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
6267 case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
6268 /* The components are premultiplied in either case and the output is
6269 * gamma encoded (to get standard Porter-Duff we expect the output
6270 * gamma to be set to 1.0!)
6271 */
6272 case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
6273 /* The optimization is that the partial-alpha entries are linear
6274 * while the opaque pixels are gamma encoded, but this only affects the
6275 * output encoding.
6276 */
6277 if (alpha < 1)
6278 {
6279 if (alpha > 0)
6280 {
6281 input_sample *= alpha;
6282 if (compose != NULL)
6283 *compose = 1;
6284 }
6285
6286 else
6287 input_sample = 0;
6288 }
6289 break;
6290#endif
6291
6292 default:
6293 /* Standard cases where no compositing is done (so the component
6294 * value is already correct.)
6295 */
6296 break;
6297 }
6298
6299 return input_sample;
6300}
6301
John Bowlerd273ad22011-05-07 21:00:28 -05006302/* This API returns the encoded *input* component, in the range 0..1 */
6303static double
6304gamma_component_validate(PNG_CONST char *name, PNG_CONST validate_info *vi,
6305 PNG_CONST unsigned int id, PNG_CONST unsigned int od,
6306 PNG_CONST double alpha /* <0 for the alpha channel itself */,
6307 PNG_CONST double background /* component background value */)
6308{
6309 PNG_CONST unsigned int isbit = id >> vi->isbit_shift;
6310 PNG_CONST unsigned int sbit_max = vi->sbit_max;
6311 PNG_CONST unsigned int outmax = vi->outmax;
6312 PNG_CONST int do_background = vi->do_background;
6313
6314 double i;
6315
6316 /* First check on the 'perfect' result obtained from the digitized input
6317 * value, id, and compare this against the actual digitized result, 'od'.
6318 * 'i' is the input result in the range 0..1:
John Bowlerd273ad22011-05-07 21:00:28 -05006319 */
6320 i = isbit; i /= sbit_max;
6321
6322 /* Check for the fast route: if we don't do any background composition or if
6323 * this is the alpha channel ('alpha' < 0) or if the pixel is opaque then
6324 * just use the gamma_correction field to correct to the final output gamma.
6325 */
6326 if (alpha == 1 /* opaque pixel component */ || !do_background
6327#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
6328 || do_background == ALPHA_MODE_OFFSET + PNG_ALPHA_PNG
6329#endif
6330 || (alpha < 0 /* alpha channel */
6331#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
6332 && do_background != ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN
6333#endif
6334 ))
6335 {
6336 /* Then get the gamma corrected version of 'i' and compare to 'od', any
6337 * error less than .5 is insignificant - just quantization of the output
6338 * value to the nearest digital value (nevertheless the error is still
6339 * recorded - it's interesting ;-)
6340 */
6341 double encoded_sample = i;
6342 double encoded_error;
6343
6344 /* alpha less than 0 indicates the alpha channel, which is always linear
6345 */
6346 if (alpha >= 0 && vi->gamma_correction > 0)
6347 encoded_sample = pow(encoded_sample, vi->gamma_correction);
6348 encoded_sample *= outmax;
6349
6350 encoded_error = fabs(od-encoded_sample);
6351
6352 if (encoded_error > vi->dp->maxerrout)
6353 vi->dp->maxerrout = encoded_error;
6354
John Bowler5441e182011-05-18 18:57:12 -05006355 if (encoded_error < vi->maxout_total)
John Bowlerd273ad22011-05-07 21:00:28 -05006356 return i;
6357 }
6358
6359 /* The slow route - attempt to do linear calculations. */
6360 /* There may be an error, or background processing is required, so calculate
6361 * the actual sample values - unencoded light intensity values. Note that in
6362 * practice these are not completely unencoded because they include a
6363 * 'viewing correction' to decrease or (normally) increase the perceptual
6364 * contrast of the image. There's nothing we can do about this - we don't
6365 * know what it is - so assume the unencoded value is perceptually linear.
6366 */
6367 {
6368 double input_sample = i; /* In range 0..1 */
6369 double output, error, encoded_sample;
6370 double es_lo, es_hi;
John Bowler5441e182011-05-18 18:57:12 -05006371 int compose = 0; /* Set to one if composition done */
6372 int output_is_encoded; /* Set if encoded to screen gamma */
John Bowlerd273ad22011-05-07 21:00:28 -05006373 int log_max_error = 1; /* Check maximum error values */
6374
6375 /* Convert to linear light (with the above caveat.) The alpha channel is
6376 * already linear.
6377 */
John Bowler5441e182011-05-18 18:57:12 -05006378 if (alpha >= 0)
6379 {
6380 int tcompose;
6381
6382 if (vi->file_inverse > 0 && input_sample > 0 && input_sample < 1)
6383 input_sample = pow(input_sample, vi->file_inverse);
6384
6385 /* Handle the compose processing: */
6386 tcompose = 0;
6387 input_sample = gamma_component_compose(do_background, input_sample,
6388 alpha, background, &tcompose);
6389
6390 if (tcompose)
6391 compose = 1;
6392 }
John Bowlerd273ad22011-05-07 21:00:28 -05006393
6394 /* And similarly for the output value, but we need to check the background
John Bowler5441e182011-05-18 18:57:12 -05006395 * handling to linearize it correctly.
John Bowlerd273ad22011-05-07 21:00:28 -05006396 */
6397 output = od;
6398 output /= outmax;
6399
John Bowler5441e182011-05-18 18:57:12 -05006400 output_is_encoded = vi->screen_gamma > 0;
John Bowlerd273ad22011-05-07 21:00:28 -05006401
John Bowler5441e182011-05-18 18:57:12 -05006402 if (alpha < 0) /* The alpha channel */
6403 {
John Bowler59a6c372011-06-11 06:51:06 -05006404#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
John Bowler5441e182011-05-18 18:57:12 -05006405 if (do_background != ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN)
John Bowler59a6c372011-06-11 06:51:06 -05006406#endif
John Bowler5441e182011-05-18 18:57:12 -05006407 {
John Bowlerd273ad22011-05-07 21:00:28 -05006408 /* In all other cases the output alpha channel is linear already,
6409 * don't log errors here, they are much larger in linear data.
6410 */
John Bowler5441e182011-05-18 18:57:12 -05006411 output_is_encoded = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05006412 log_max_error = 0;
John Bowler5441e182011-05-18 18:57:12 -05006413 }
John Bowlerd273ad22011-05-07 21:00:28 -05006414 }
6415
John Bowler59a6c372011-06-11 06:51:06 -05006416#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
John Bowler5441e182011-05-18 18:57:12 -05006417 else /* A component */
John Bowlerd273ad22011-05-07 21:00:28 -05006418 {
John Bowler5441e182011-05-18 18:57:12 -05006419 if (do_background == ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED &&
6420 alpha < 1) /* the optimized case - linear output */
6421 {
6422 if (alpha > 0) log_max_error = 0;
6423 output_is_encoded = 0;
6424 }
John Bowlerd273ad22011-05-07 21:00:28 -05006425 }
John Bowler59a6c372011-06-11 06:51:06 -05006426#endif
John Bowlerd273ad22011-05-07 21:00:28 -05006427
John Bowler5441e182011-05-18 18:57:12 -05006428 if (output_is_encoded)
6429 output = pow(output, vi->screen_gamma);
6430
John Bowlerd273ad22011-05-07 21:00:28 -05006431 /* Calculate (or recalculate) the encoded_sample value and repeat the
6432 * check above (unnecessary if we took the fast route, but harmless.)
6433 */
6434 encoded_sample = input_sample;
6435 if (output_is_encoded)
6436 encoded_sample = pow(encoded_sample, vi->screen_inverse);
6437 encoded_sample *= outmax;
6438
6439 {
6440 PNG_CONST double encoded_error = fabs(od-encoded_sample);
6441
6442 /* Don't log errors in the alpha channel, or the 'optimized' case,
6443 * neither are significant to the overall perception.
6444 */
6445 if (log_max_error && encoded_error > vi->dp->maxerrout)
6446 vi->dp->maxerrout = encoded_error;
6447
John Bowler5441e182011-05-18 18:57:12 -05006448 if (encoded_error < vi->maxout_total)
John Bowlerd273ad22011-05-07 21:00:28 -05006449 return i;
6450 }
6451
John Bowler5441e182011-05-18 18:57:12 -05006452 /* i: the original input value in the range 0..1
6453 *
6454 * pngvalid calculations:
6455 * input_sample: linear result; i linearized and composed, range 0..1
6456 * encoded_sample: encoded result; input_sample scaled to ouput bit depth
6457 *
6458 * libpng calculations:
6459 * output: linear result; od scaled to 0..1 and linearized
6460 * od: encoded result from libpng
6461 */
6462
John Bowlerd273ad22011-05-07 21:00:28 -05006463 /* Now we have the numbers for real errors, both absolute values as as a
6464 * percentage of the correct value (output):
6465 */
6466 error = fabs(input_sample-output);
6467
6468 if (log_max_error && error > vi->dp->maxerrabs)
6469 vi->dp->maxerrabs = error;
6470
6471 /* The following is an attempt to ignore the tendency of quantization to
John Bowler5441e182011-05-18 18:57:12 -05006472 * dominate the percentage errors for lower result values:
John Bowlerd273ad22011-05-07 21:00:28 -05006473 */
John Bowler5441e182011-05-18 18:57:12 -05006474 if (log_max_error && input_sample > .5)
John Bowlerd273ad22011-05-07 21:00:28 -05006475 {
6476 double percentage_error = error/input_sample;
6477 if (percentage_error > vi->dp->maxerrpc)
6478 vi->dp->maxerrpc = percentage_error;
6479 }
6480
6481 /* Now calculate the digitization limits for 'encoded_sample' using the
6482 * 'max' values. Note that maxout is in the encoded space but maxpc and
6483 * maxabs are in linear light space.
6484 *
6485 * First find the maximum error in linear light space, range 0..1:
6486 */
6487 {
6488 double tmp = input_sample * vi->maxpc;
6489 if (tmp < vi->maxabs) tmp = vi->maxabs;
John Bowler5441e182011-05-18 18:57:12 -05006490 /* If 'compose' is true the composition was done in linear space using
6491 * integer arithmetic. This introduces an extra error of +/- 0.5 (at
6492 * least) in the integer space used. 'maxcalc' records this, taking
6493 * into account the possibility that even for 16 bit output 8 bit space
6494 * may have been used.
6495 */
John Bowlerd273ad22011-05-07 21:00:28 -05006496 if (compose && tmp < vi->maxcalc) tmp = vi->maxcalc;
6497
John Bowler5441e182011-05-18 18:57:12 -05006498 /* The 'maxout' value refers to the encoded result, to compare with
6499 * this encode input_sample adjusted by the maximum error (tmp) above.
6500 */
John Bowlerd273ad22011-05-07 21:00:28 -05006501 es_lo = encoded_sample - vi->maxout;
6502
6503 if (es_lo > 0 && input_sample-tmp > 0)
6504 {
6505 double low_value = input_sample-tmp;
6506 if (output_is_encoded)
6507 low_value = pow(low_value, vi->screen_inverse);
6508 low_value *= outmax;
6509 if (low_value < es_lo) es_lo = low_value;
John Bowler5441e182011-05-18 18:57:12 -05006510
6511 /* Quantize this appropriately: */
6512 es_lo = ceil(es_lo / vi->outquant - .5) * vi->outquant;
John Bowlerd273ad22011-05-07 21:00:28 -05006513 }
6514
6515 else
6516 es_lo = 0;
6517
6518 es_hi = encoded_sample + vi->maxout;
6519
6520 if (es_hi < outmax && input_sample+tmp < 1)
6521 {
6522 double high_value = input_sample+tmp;
6523 if (output_is_encoded)
6524 high_value = pow(high_value, vi->screen_inverse);
6525 high_value *= outmax;
6526 if (high_value > es_hi) es_hi = high_value;
John Bowler5441e182011-05-18 18:57:12 -05006527
6528 es_hi = floor(es_hi / vi->outquant + .5) * vi->outquant;
John Bowlerd273ad22011-05-07 21:00:28 -05006529 }
6530
6531 else
6532 es_hi = outmax;
6533 }
6534
6535 /* The primary test is that the final encoded value returned by the
6536 * library should be between the two limits (inclusive) that were
John Bowler5441e182011-05-18 18:57:12 -05006537 * calculated above.
John Bowlerd273ad22011-05-07 21:00:28 -05006538 */
John Bowler5441e182011-05-18 18:57:12 -05006539 if (od < es_lo || od > es_hi)
John Bowlerd273ad22011-05-07 21:00:28 -05006540 {
6541 /* There has been an error in processing. */
6542 double is_lo, is_hi;
6543
John Bowlerd273ad22011-05-07 21:00:28 -05006544 if (vi->use_input_precision)
6545 {
6546 /* Ok, something is wrong - this actually happens in current libpng
John Bowler5441e182011-05-18 18:57:12 -05006547 * 16-to-8 processing. Assume that the input value (id, adjusted
6548 * for sbit) can be anywhere between value-.5 and value+.5 - quite a
John Bowlerd273ad22011-05-07 21:00:28 -05006549 * large range if sbit is low.
6550 */
6551 double tmp = (isbit - .5)/sbit_max;
6552
John Bowler5441e182011-05-18 18:57:12 -05006553 if (tmp <= 0)
6554 tmp = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05006555
John Bowler5441e182011-05-18 18:57:12 -05006556 else if (alpha >= 0 && vi->file_inverse > 0 && tmp < 1)
6557 tmp = pow(tmp, vi->file_inverse);
6558
6559 tmp = gamma_component_compose(do_background, tmp, alpha, background,
6560 NULL);
6561
6562 if (output_is_encoded && tmp > 0 && tmp < 1)
6563 tmp = pow(tmp, vi->screen_inverse);
6564
6565 is_lo = ceil(outmax * tmp - vi->maxout_total);
6566
6567 if (is_lo < 0)
John Bowlerd273ad22011-05-07 21:00:28 -05006568 is_lo = 0;
6569
6570 tmp = (isbit + .5)/sbit_max;
6571
John Bowler5441e182011-05-18 18:57:12 -05006572 if (tmp <= 0)
6573 tmp = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05006574
John Bowler5441e182011-05-18 18:57:12 -05006575 else if (alpha >= 0 && vi->file_inverse > 0 && tmp < 1)
6576 tmp = pow(tmp, vi->file_inverse);
6577
6578 tmp = gamma_component_compose(do_background, tmp, alpha, background,
6579 NULL);
6580
6581 if (output_is_encoded && tmp > 0 && tmp < 1)
6582 tmp = pow(tmp, vi->screen_inverse);
6583
6584 is_hi = floor(outmax * tmp + vi->maxout_total);
6585
6586 if (is_hi > outmax)
John Bowlerd273ad22011-05-07 21:00:28 -05006587 is_hi = outmax;
6588
John Bowler5441e182011-05-18 18:57:12 -05006589 if (!(od < is_lo || od > is_hi))
John Bowlerd273ad22011-05-07 21:00:28 -05006590 return i;
6591
6592 /* One last chance. If this is an alpha channel and the 16to8
6593 * option has been used and 'inaccurate' scaling is used then the
6594 * bit reduction is obtained by simply using the top 8 bits of the
6595 * value.
6596 */
6597# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
6598 /* This may be required for other components in the future, but
6599 * at present the presence of gamma correction effectively
6600 * prevents the errors in the component scaling (I don't quite
6601 * understand why, but since it's better this way I care not to
6602 * ask, JB 20110419.)
6603 */
6604 if (alpha < 0 && vi->strip16 && vi->sbit > 8 &&
6605 vi->sbit + vi->isbit_shift == 16)
6606 {
6607 tmp = ((id >> 8) - .5)/255;
6608
6609 if (tmp > 0)
6610 {
John Bowler5441e182011-05-18 18:57:12 -05006611 is_lo = ceil(outmax * tmp - vi->maxout_total);
John Bowlerd273ad22011-05-07 21:00:28 -05006612 if (is_lo < 0) is_lo = 0;
6613 }
6614
6615 else
6616 is_lo = 0;
6617
6618 tmp = ((id >> 8) + .5)/255;
6619
6620 if (tmp < 1)
6621 {
John Bowler5441e182011-05-18 18:57:12 -05006622 is_hi = floor(outmax * tmp + vi->maxout_total);
John Bowlerd273ad22011-05-07 21:00:28 -05006623 if (is_hi > outmax) is_hi = outmax;
6624 }
6625
6626 else
6627 is_hi = outmax;
6628
John Bowler5441e182011-05-18 18:57:12 -05006629 if (!(od < is_lo || od > is_hi))
John Bowlerd273ad22011-05-07 21:00:28 -05006630 return i;
6631 }
6632# endif
6633 }
6634 else /* !use_input_precision */
6635 is_lo = es_lo, is_hi = es_hi;
6636
6637 {
6638 char msg[256];
6639
6640 sprintf(msg, "%s: %.3f; %u*%.3f{%u;%u} -> %u not %.2f (%.1f-%.1f)",
6641 name, od-encoded_sample, id, alpha, vi->sbit, isbit, od,
6642 encoded_sample, is_lo, is_hi);
6643
6644# ifdef PNG_WARNINGS_SUPPORTED
6645 png_warning(vi->pp, msg);
6646# else
6647 store_warning(vi->pp, msg);
6648# endif
6649 }
6650 }
6651 }
6652
6653 return i;
6654}
6655
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006656static void
John Bowler9994f252011-05-15 18:52:39 -05006657gamma_image_validate(gamma_display *dp, png_structp pp, png_infop pi)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006658{
6659 /* Get some constants derived from the input and output file formats: */
John Bowler9994f252011-05-15 18:52:39 -05006660 PNG_CONST png_store* PNG_CONST ps = dp->this.ps;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006661 PNG_CONST png_byte in_ct = dp->this.colour_type;
6662 PNG_CONST png_byte in_bd = dp->this.bit_depth;
6663 PNG_CONST png_uint_32 w = dp->this.w;
6664 PNG_CONST png_uint_32 h = dp->this.h;
6665 PNG_CONST size_t cbRow = dp->this.cbRow;
6666 PNG_CONST png_byte out_ct = png_get_color_type(pp, pi);
6667 PNG_CONST png_byte out_bd = png_get_bit_depth(pp, pi);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006668
6669 /* There are three sources of error, firstly the quantization in the
6670 * file encoding, determined by sbit and/or the file depth, secondly
6671 * the output (screen) gamma and thirdly the output file encoding.
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006672 *
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006673 * Since this API receives the screen and file gamma in double
6674 * precision it is possible to calculate an exact answer given an input
6675 * pixel value. Therefore we assume that the *input* value is exact -
6676 * sample/maxsample - calculate the corresponding gamma corrected
6677 * output to the limits of double precision arithmetic and compare with
6678 * what libpng returns.
6679 *
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006680 * Since the library must quantize the output to 8 or 16 bits there is
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006681 * a fundamental limit on the accuracy of the output of +/-.5 - this
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006682 * quantization limit is included in addition to the other limits
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006683 * specified by the paramaters to the API. (Effectively, add .5
6684 * everywhere.)
6685 *
6686 * The behavior of the 'sbit' paramter is defined by section 12.5
6687 * (sample depth scaling) of the PNG spec. That section forces the
6688 * decoder to assume that the PNG values have been scaled if sBIT is
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006689 * present:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006690 *
Glenn Randers-Pehrsonbc363ec2010-10-12 21:17:00 -05006691 * png-sample = floor( input-sample * (max-out/max-in) + .5);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006692 *
6693 * This means that only a subset of the possible PNG values should
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006694 * appear in the input. However, the spec allows the encoder to use a
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006695 * variety of approximations to the above and doesn't require any
6696 * restriction of the values produced.
6697 *
6698 * Nevertheless the spec requires that the upper 'sBIT' bits of the
6699 * value stored in a PNG file be the original sample bits.
6700 * Consequently the code below simply scales the top sbit bits by
6701 * (1<<sbit)-1 to obtain an original sample value.
6702 *
6703 * Because there is limited precision in the input it is arguable that
6704 * an acceptable result is any valid result from input-.5 to input+.5.
John Bowlerd273ad22011-05-07 21:00:28 -05006705 * The basic tests below do not do this, however if 'use_input_precision'
6706 * is set a subsequent test is performed below.
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006707 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006708 PNG_CONST unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U;
John Bowlerd273ad22011-05-07 21:00:28 -05006709 int processing;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006710 png_uint_32 y;
John Bowler5441e182011-05-18 18:57:12 -05006711 PNG_CONST store_palette_entry *in_palette = dp->this.palette;
6712 PNG_CONST int in_is_transparent = dp->this.is_transparent;
6713 int out_npalette = -1;
6714 int out_is_transparent = 0; /* Just refers to the palette case */
6715 store_palette out_palette;
John Bowlerd273ad22011-05-07 21:00:28 -05006716 validate_info vi;
6717
John Bowler9994f252011-05-15 18:52:39 -05006718 /* Check for row overwrite errors */
6719 store_image_check(dp->this.ps, pp, 0);
6720
John Bowler5441e182011-05-18 18:57:12 -05006721 /* Supply the input and output sample depths here - 8 for an indexed image,
6722 * otherwise the bit depth.
6723 */
6724 init_validate_info(&vi, dp, pp, in_ct==3?8:in_bd, out_ct==3?8:out_bd);
John Bowlerd273ad22011-05-07 21:00:28 -05006725
John Bowler5441e182011-05-18 18:57:12 -05006726 processing = (vi.gamma_correction > 0 && !dp->threshold_test)
John Bowlered4d32b2011-05-11 23:02:28 -05006727 || in_bd != out_bd || in_ct != out_ct || vi.do_background;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006728
John Bowler5441e182011-05-18 18:57:12 -05006729 /* TODO: FIX THIS: MAJOR BUG! If the transformations all happen inside
6730 * the palette there is no way of finding out, because libpng fails to
6731 * update the palette on png_read_update_info. Indeed, libpng doesn't
6732 * even do the required work until much later, when it doesn't have any
6733 * info pointer. Oops. For the moment 'processing' is turned off if
6734 * out_ct is palette.
6735 */
6736 if (in_ct == 3 && out_ct == 3)
6737 processing = 0;
6738
6739 if (processing && out_ct == 3)
6740 out_is_transparent = read_palette(out_palette, &out_npalette, pp, pi);
6741
John Bowler9994f252011-05-15 18:52:39 -05006742 for (y=0; y<h; ++y)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006743 {
John Bowler9994f252011-05-15 18:52:39 -05006744 png_const_bytep pRow = store_image_row(ps, pp, 0, y);
John Bowler660c6e42010-12-19 06:22:23 -06006745 png_byte std[STANDARD_ROWMAX];
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006746
John Bowler660c6e42010-12-19 06:22:23 -06006747 transform_row(pp, std, in_ct, in_bd, y);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006748
6749 if (processing)
6750 {
John Bowlerd273ad22011-05-07 21:00:28 -05006751 unsigned int x;
6752
6753 for (x=0; x<w; ++x)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006754 {
John Bowlerd273ad22011-05-07 21:00:28 -05006755 double alpha = 1; /* serves as a flag value */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006756
John Bowler5441e182011-05-18 18:57:12 -05006757 /* Record the palette index for index images. */
6758 PNG_CONST unsigned int in_index =
6759 in_ct == 3 ? sample(std, 3, in_bd, x, 0) : 256;
6760 PNG_CONST unsigned int out_index =
6761 out_ct == 3 ? sample(std, 3, out_bd, x, 0) : 256;
6762
John Bowlerd273ad22011-05-07 21:00:28 -05006763 /* Handle input alpha - png_set_background will cause the output
6764 * alpha to disappear so there is nothing to check.
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006765 */
John Bowler5441e182011-05-18 18:57:12 -05006766 if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 || (in_ct == 3 &&
6767 in_is_transparent))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006768 {
John Bowler5441e182011-05-18 18:57:12 -05006769 PNG_CONST unsigned int input_alpha = in_ct == 3 ?
6770 dp->this.palette[in_index].alpha :
John Bowlerd273ad22011-05-07 21:00:28 -05006771 sample(std, in_ct, in_bd, x, samples_per_pixel);
6772
John Bowler5441e182011-05-18 18:57:12 -05006773 unsigned int output_alpha = 65536 /* as a flag value */;
6774
6775 if (out_ct == 3)
John Bowlerd273ad22011-05-07 21:00:28 -05006776 {
John Bowler5441e182011-05-18 18:57:12 -05006777 if (out_is_transparent)
6778 output_alpha = out_palette[out_index].alpha;
6779 }
6780
6781 else if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0)
6782 output_alpha = sample(pRow, out_ct, out_bd, x,
6783 samples_per_pixel);
6784
6785 if (output_alpha != 65536)
6786 alpha = gamma_component_validate("alpha", &vi, input_alpha,
6787 output_alpha, -1/*alpha*/, 0/*background*/);
6788
6789 else /* no alpha in output */
6790 {
6791 /* This is a copy of the calculation of 'i' above in order to
6792 * have the alpha value to use in the background calculation.
6793 */
John Bowlerd273ad22011-05-07 21:00:28 -05006794 alpha = input_alpha >> vi.isbit_shift;
6795 alpha /= vi.sbit_max;
6796 }
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006797 }
6798
John Bowlerd273ad22011-05-07 21:00:28 -05006799 /* Handle greyscale or RGB components. */
6800 if ((in_ct & PNG_COLOR_MASK_COLOR) == 0) /* greyscale */
6801 (void)gamma_component_validate("gray", &vi,
6802 sample(std, in_ct, in_bd, x, 0),
6803 sample(pRow, out_ct, out_bd, x, 0), alpha/*component*/,
6804 vi.background_red);
John Bowler5441e182011-05-18 18:57:12 -05006805 else /* RGB or palette */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006806 {
John Bowlerd273ad22011-05-07 21:00:28 -05006807 (void)gamma_component_validate("red", &vi,
John Bowler5441e182011-05-18 18:57:12 -05006808 in_ct == 3 ? in_palette[in_index].red :
6809 sample(std, in_ct, in_bd, x, 0),
6810 out_ct == 3 ? out_palette[out_index].red :
6811 sample(pRow, out_ct, out_bd, x, 0),
6812 alpha/*component*/, vi.background_red);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006813
John Bowlerd273ad22011-05-07 21:00:28 -05006814 (void)gamma_component_validate("green", &vi,
John Bowler5441e182011-05-18 18:57:12 -05006815 in_ct == 3 ? in_palette[in_index].green :
6816 sample(std, in_ct, in_bd, x, 1),
6817 out_ct == 3 ? out_palette[out_index].green :
6818 sample(pRow, out_ct, out_bd, x, 1),
6819 alpha/*component*/, vi.background_green);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006820
John Bowlerd273ad22011-05-07 21:00:28 -05006821 (void)gamma_component_validate("blue", &vi,
John Bowler5441e182011-05-18 18:57:12 -05006822 in_ct == 3 ? in_palette[in_index].blue :
6823 sample(std, in_ct, in_bd, x, 2),
6824 out_ct == 3 ? out_palette[out_index].blue :
6825 sample(pRow, out_ct, out_bd, x, 2),
6826 alpha/*component*/, vi.background_blue);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006827 }
6828 }
6829 }
6830
John Bowlered4d32b2011-05-11 23:02:28 -05006831 else if (memcmp(std, pRow, cbRow) != 0)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006832 {
6833 char msg[64];
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006834
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006835 /* No transform is expected on the threshold tests. */
6836 sprintf(msg, "gamma: below threshold row %d changed", y);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006837
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006838 png_error(pp, msg);
6839 }
6840 } /* row (y) loop */
6841
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006842 dp->this.ps->validated = 1;
6843}
6844
6845static void
6846gamma_end(png_structp pp, png_infop pi)
6847{
6848 gamma_display *dp = png_get_progressive_ptr(pp);
6849
John Bowler9994f252011-05-15 18:52:39 -05006850 if (!dp->this.speed)
6851 gamma_image_validate(dp, pp, pi);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006852}
6853
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05006854/* A single test run checking a gamma transformation.
6855 *
6856 * maxabs: maximum absolute error as a fraction
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006857 * maxout: maximum output error in the output units
6858 * maxpc: maximum percentage error (as a percentage)
6859 */
6860static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006861gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn,
John Bowler9994f252011-05-15 18:52:39 -05006862 PNG_CONST png_byte bit_depthIn, PNG_CONST int palette_numberIn,
6863 PNG_CONST int interlace_typeIn,
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006864 PNG_CONST double file_gammaIn, PNG_CONST double screen_gammaIn,
6865 PNG_CONST png_byte sbitIn, PNG_CONST int threshold_testIn,
John Bowler9994f252011-05-15 18:52:39 -05006866 PNG_CONST char *name,
John Bowlerd273ad22011-05-07 21:00:28 -05006867 PNG_CONST int use_input_precisionIn, PNG_CONST int strip16In,
6868 PNG_CONST int expand16In, PNG_CONST int do_backgroundIn,
6869 PNG_CONST png_color_16 *bkgd_colorIn, double bkgd_gammaIn)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006870{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006871 gamma_display d;
6872 context(&pmIn->this, fault);
6873
John Bowler660c6e42010-12-19 06:22:23 -06006874 gamma_display_init(&d, pmIn, FILEID(colour_typeIn, bit_depthIn,
John Bowler9994f252011-05-15 18:52:39 -05006875 palette_numberIn, interlace_typeIn, 0, 0, 0),
6876 file_gammaIn, screen_gammaIn, sbitIn,
6877 threshold_testIn, use_input_precisionIn, strip16In,
John Bowlerd273ad22011-05-07 21:00:28 -05006878 expand16In, do_backgroundIn, bkgd_colorIn, bkgd_gammaIn);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006879
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006880 Try
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006881 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006882 png_structp pp;
6883 png_infop pi;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006884 gamma_modification gamma_mod;
6885 srgb_modification srgb_mod;
6886 sbit_modification sbit_mod;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006887
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006888 /* Make an appropriate modifier to set the PNG file gamma to the
6889 * given gamma value and the sBIT chunk to the given precision.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006890 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006891 d.pm->modifications = NULL;
6892 gamma_modification_init(&gamma_mod, d.pm, d.file_gamma);
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06006893 srgb_modification_init(&srgb_mod, d.pm, 127 /*delete*/);
John Bowler5441e182011-05-18 18:57:12 -05006894 if (d.sbit > 0)
6895 sbit_modification_init(&sbit_mod, d.pm, d.sbit);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006896
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006897 modification_reset(d.pm->modifications);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006898
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05006899 /* Get a png_struct for writing the image. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006900 pp = set_modifier_for_read(d.pm, &pi, d.this.id, name);
John Bowler9994f252011-05-15 18:52:39 -05006901 standard_palette_init(&d.this);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05006902
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006903 /* Introduce the correct read function. */
6904 if (d.pm->this.progressive)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006905 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006906 /* Share the row function with the standard implementation. */
6907 png_set_progressive_read_fn(pp, &d, gamma_info, progressive_row,
6908 gamma_end);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006909
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006910 /* Now feed data into the reader until we reach the end: */
6911 modifier_progressive_read(d.pm, pp, pi);
6912 }
6913 else
6914 {
6915 /* modifier_read expects a png_modifier* */
6916 png_set_read_fn(pp, d.pm, modifier_read);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006917
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006918 /* Check the header values: */
6919 png_read_info(pp, pi);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006920
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006921 /* Process the 'info' requirements. Only one image is generated */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006922 gamma_info_imp(&d, pp, pi);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006923
John Bowler9994f252011-05-15 18:52:39 -05006924 sequential_row(&d.this, pp, pi, -1, 0);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006925
John Bowler9994f252011-05-15 18:52:39 -05006926 if (!d.this.speed)
6927 gamma_image_validate(&d, pp, pi);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006928 }
6929
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006930 modifier_reset(d.pm);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006931
John Bowler9994f252011-05-15 18:52:39 -05006932 if (d.pm->log && !d.threshold_test && !d.this.speed)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006933 fprintf(stderr, "%d bit %s %s: max error %f (%.2g, %2g%%)\n",
John Bowlerd273ad22011-05-07 21:00:28 -05006934 d.this.bit_depth, colour_types[d.this.colour_type], name,
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006935 d.maxerrout, d.maxerrabs, 100*d.maxerrpc);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006936
6937 /* Log the summary values too. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006938 if (d.this.colour_type == 0 || d.this.colour_type == 4)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006939 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006940 switch (d.this.bit_depth)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05006941 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006942 case 1:
6943 break;
6944
6945 case 2:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006946 if (d.maxerrout > d.pm->error_gray_2)
6947 d.pm->error_gray_2 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006948
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006949 break;
6950
6951 case 4:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006952 if (d.maxerrout > d.pm->error_gray_4)
6953 d.pm->error_gray_4 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006954
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006955 break;
6956
6957 case 8:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006958 if (d.maxerrout > d.pm->error_gray_8)
6959 d.pm->error_gray_8 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006960
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006961 break;
6962
6963 case 16:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006964 if (d.maxerrout > d.pm->error_gray_16)
6965 d.pm->error_gray_16 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006966
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006967 break;
6968
6969 default:
6970 png_error(pp, "bad bit depth (internal: 1)");
6971 }
6972 }
6973
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006974 else if (d.this.colour_type == 2 || d.this.colour_type == 6)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006975 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006976 switch (d.this.bit_depth)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006977 {
6978 case 8:
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006979
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006980 if (d.maxerrout > d.pm->error_color_8)
6981 d.pm->error_color_8 = d.maxerrout;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006982
6983 break;
6984
6985 case 16:
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006986
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006987 if (d.maxerrout > d.pm->error_color_16)
6988 d.pm->error_color_16 = d.maxerrout;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006989
6990 break;
6991
6992 default:
6993 png_error(pp, "bad bit depth (internal: 2)");
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05006994 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006995 }
John Bowler1921e6d2011-05-16 20:57:54 -05006996
6997 else if (d.this.colour_type == 3)
6998 {
6999 if (d.maxerrout > d.pm->error_indexed)
7000 d.pm->error_indexed = d.maxerrout;
7001 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007002 }
7003
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007004 Catch(fault)
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05007005 modifier_reset((png_modifier*)fault);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007006}
7007
7008static void gamma_threshold_test(png_modifier *pm, png_byte colour_type,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05007009 png_byte bit_depth, int interlace_type, double file_gamma,
7010 double screen_gamma)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007011{
7012 size_t pos = 0;
7013 char name[64];
7014 pos = safecat(name, sizeof name, pos, "threshold ");
7015 pos = safecatd(name, sizeof name, pos, file_gamma, 3);
7016 pos = safecat(name, sizeof name, pos, "/");
7017 pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
7018
John Bowler9994f252011-05-15 18:52:39 -05007019 (void)gamma_test(pm, colour_type, bit_depth, 0/*palette*/, interlace_type,
John Bowler5441e182011-05-18 18:57:12 -05007020 file_gamma, screen_gamma, 0/*sBIT*/, 1/*threshold test*/, name,
John Bowler9994f252011-05-15 18:52:39 -05007021 0 /*no input precision*/,
John Bowlerd273ad22011-05-07 21:00:28 -05007022 0 /*no strip16*/, 0 /*no expand16*/, 0 /*no background*/, 0 /*hence*/,
7023 0 /*no background gamma*/);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007024}
7025
7026static void
7027perform_gamma_threshold_tests(png_modifier *pm)
7028{
7029 png_byte colour_type = 0;
7030 png_byte bit_depth = 0;
John Bowler9994f252011-05-15 18:52:39 -05007031 int palette_number = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007032
John Bowler9994f252011-05-15 18:52:39 -05007033 /* Don't test more than one instance of each palette - it's pointless, in
7034 * fact this test is somewhat excessive since libpng doesn't make this
7035 * decision based on colour type or bit depth!
7036 */
7037 while (next_format(&colour_type, &bit_depth, &palette_number))
7038 if (palette_number == 0)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007039 {
John Bowler168a4332011-01-16 19:32:22 -06007040 double test_gamma = 1.0;
7041 while (test_gamma >= .4)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007042 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05007043 /* There's little point testing the interlacing vs non-interlacing,
7044 * but this can be set from the command line.
7045 */
7046 gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
John Bowler168a4332011-01-16 19:32:22 -06007047 test_gamma, 1/test_gamma);
7048 test_gamma *= .95;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007049 }
7050
7051 /* And a special test for sRGB */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05007052 gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
7053 .45455, 2.2);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007054
7055 if (fail(pm))
7056 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007057 }
7058}
7059
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05007060static void gamma_transform_test(png_modifier *pm,
7061 PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth,
John Bowler9994f252011-05-15 18:52:39 -05007062 PNG_CONST int palette_number,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05007063 PNG_CONST int interlace_type, PNG_CONST double file_gamma,
John Bowler9994f252011-05-15 18:52:39 -05007064 PNG_CONST double screen_gamma, PNG_CONST png_byte sbit,
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05007065 PNG_CONST int use_input_precision, PNG_CONST int strip16)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007066{
7067 size_t pos = 0;
7068 char name[64];
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007069
John Bowler5441e182011-05-18 18:57:12 -05007070 if (sbit != bit_depth && sbit != 0)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007071 {
7072 pos = safecat(name, sizeof name, pos, "sbit(");
7073 pos = safecatn(name, sizeof name, pos, sbit);
7074 pos = safecat(name, sizeof name, pos, ") ");
7075 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007076
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007077 else
7078 pos = safecat(name, sizeof name, pos, "gamma ");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007079
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007080 if (strip16)
7081 pos = safecat(name, sizeof name, pos, "16to8 ");
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007082
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007083 pos = safecatd(name, sizeof name, pos, file_gamma, 3);
7084 pos = safecat(name, sizeof name, pos, "->");
7085 pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
7086
John Bowler9994f252011-05-15 18:52:39 -05007087 gamma_test(pm, colour_type, bit_depth, palette_number, interlace_type,
7088 file_gamma, screen_gamma, sbit, 0, name, use_input_precision,
7089 strip16, pm->test_gamma_expand16, 0 , 0, 0);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007090}
7091
John Bowler9994f252011-05-15 18:52:39 -05007092static void perform_gamma_transform_tests(png_modifier *pm)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007093{
7094 png_byte colour_type = 0;
7095 png_byte bit_depth = 0;
John Bowler9994f252011-05-15 18:52:39 -05007096 int palette_number = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007097
John Bowler9994f252011-05-15 18:52:39 -05007098 while (next_format(&colour_type, &bit_depth, &palette_number))
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007099 {
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05007100 unsigned int i, j;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007101
7102 for (i=0; i<pm->ngammas; ++i) for (j=0; j<pm->ngammas; ++j) if (i != j)
7103 {
John Bowler9994f252011-05-15 18:52:39 -05007104 gamma_transform_test(pm, colour_type, bit_depth, palette_number,
John Bowler5441e182011-05-18 18:57:12 -05007105 pm->interlace_type, 1/pm->gammas[i], pm->gammas[j], 0/*sBIT*/,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06007106 pm->use_input_precision, 0 /*do not strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007107
7108 if (fail(pm))
7109 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007110 }
7111 }
7112}
7113
John Bowler9994f252011-05-15 18:52:39 -05007114static void perform_gamma_sbit_tests(png_modifier *pm)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007115{
7116 png_byte sbit;
7117
7118 /* The only interesting cases are colour and grayscale, alpha is ignored here
John Bowler9994f252011-05-15 18:52:39 -05007119 * for overall speed. Only bit depths where sbit is less than the bit depth
7120 * are tested.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007121 */
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05007122 for (sbit=pm->sbitlow; sbit<(1<<READ_BDHI); ++sbit)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007123 {
John Bowler9994f252011-05-15 18:52:39 -05007124 png_byte colour_type, bit_depth;
7125 int npalette;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007126
John Bowler9994f252011-05-15 18:52:39 -05007127 colour_type = bit_depth = 0;
7128 npalette = 0;
7129
7130 while (next_format(&colour_type, &bit_depth, &npalette))
7131 if ((colour_type & PNG_COLOR_MASK_ALPHA) == 0 &&
7132 ((colour_type == 3 && sbit < 8) ||
7133 (colour_type != 3 && sbit < bit_depth)))
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007134 {
John Bowler9994f252011-05-15 18:52:39 -05007135 unsigned int i;
7136
7137 for (i=0; i<pm->ngammas; ++i)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05007138 {
John Bowler9994f252011-05-15 18:52:39 -05007139 unsigned int j;
7140
7141 for (j=0; j<pm->ngammas; ++j) if (i != j)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007142 {
John Bowler9994f252011-05-15 18:52:39 -05007143 gamma_transform_test(pm, colour_type, bit_depth, npalette,
7144 pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
7145 sbit, pm->use_input_precision_sbit, 0 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007146
7147 if (fail(pm))
7148 return;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007149 }
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05007150 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007151 }
7152 }
7153}
7154
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05007155/* Note that this requires a 16 bit source image but produces 8 bit output, so
John Bowler9994f252011-05-15 18:52:39 -05007156 * we only need the 16bit write support, but the 16 bit images are only
7157 * generated if DO_16BIT is defined.
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05007158 */
John Bowler9994f252011-05-15 18:52:39 -05007159#ifdef DO_16BIT
7160static void perform_gamma_strip16_tests(png_modifier *pm)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007161{
7162# ifndef PNG_MAX_GAMMA_8
7163# define PNG_MAX_GAMMA_8 11
7164# endif
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007165 /* Include the alpha cases here. Note that sbit matches the internal value
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007166 * used by the library - otherwise we will get spurious errors from the
7167 * internal sbit style approximation.
7168 *
Glenn Randers-Pehrson233357e2010-07-29 21:49:38 -05007169 * The threshold test is here because otherwise the 16 to 8 conversion will
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007170 * proceed *without* gamma correction, and the tests above will fail (but not
7171 * by much) - this could be fixed, it only appears with the -g option.
7172 */
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05007173 unsigned int i, j;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007174 for (i=0; i<pm->ngammas; ++i)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007175 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007176 for (j=0; j<pm->ngammas; ++j)
7177 {
7178 if (i != j &&
7179 fabs(pm->gammas[j]/pm->gammas[i]-1) >= PNG_GAMMA_THRESHOLD)
7180 {
John Bowler9994f252011-05-15 18:52:39 -05007181 gamma_transform_test(pm, 0, 16, 0, pm->interlace_type,
7182 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
7183 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007184
7185 if (fail(pm))
7186 return;
7187
John Bowler9994f252011-05-15 18:52:39 -05007188 gamma_transform_test(pm, 2, 16, 0, pm->interlace_type,
7189 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
7190 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007191
7192 if (fail(pm))
7193 return;
7194
John Bowler9994f252011-05-15 18:52:39 -05007195 gamma_transform_test(pm, 4, 16, 0, pm->interlace_type,
7196 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
7197 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007198
7199 if (fail(pm))
7200 return;
7201
John Bowler9994f252011-05-15 18:52:39 -05007202 gamma_transform_test(pm, 6, 16, 0, pm->interlace_type,
7203 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
7204 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007205
7206 if (fail(pm))
7207 return;
7208 }
7209 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007210 }
7211}
Glenn Randers-Pehrsonfded04f2010-08-27 14:21:21 -05007212#endif /* 16 to 8 bit conversion */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007213
John Bowler9994f252011-05-15 18:52:39 -05007214#if defined PNG_READ_BACKGROUND_SUPPORTED ||\
7215 defined PNG_READ_ALPHA_MODE_SUPPORTED
John Bowlerd273ad22011-05-07 21:00:28 -05007216static void gamma_composition_test(png_modifier *pm,
7217 PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth,
John Bowler9994f252011-05-15 18:52:39 -05007218 PNG_CONST int palette_number,
John Bowlerd273ad22011-05-07 21:00:28 -05007219 PNG_CONST int interlace_type, PNG_CONST double file_gamma,
John Bowler9994f252011-05-15 18:52:39 -05007220 PNG_CONST double screen_gamma,
John Bowlerd273ad22011-05-07 21:00:28 -05007221 PNG_CONST int use_input_precision, PNG_CONST int do_background,
7222 PNG_CONST int expand_16)
7223{
7224 size_t pos = 0;
7225 png_const_charp base;
7226 double bg;
7227 char name[128];
7228 png_color_16 background;
7229
7230 /* Make up a name and get an appropriate background gamma value. */
7231 switch (do_background)
7232 {
7233 default:
7234 base = "gamma ";
7235 bg = 4; /* should not be used */
7236 break;
7237 case PNG_BACKGROUND_GAMMA_SCREEN:
7238 base = "background(screen) ";
7239 bg = 1/screen_gamma;
7240 break;
7241 case PNG_BACKGROUND_GAMMA_FILE:
7242 base = "background(file) ";
7243 bg = file_gamma;
7244 break;
7245 case PNG_BACKGROUND_GAMMA_UNIQUE:
7246 base = "background(unique) ";
7247 /* This tests the handling of a unique value, the math is such that the
7248 * value tends to be <1, but is neither screen nor file (even if they
7249 * match!)
7250 */
7251 bg = (file_gamma + screen_gamma) / 3;
7252 break;
7253#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
7254 case ALPHA_MODE_OFFSET + PNG_ALPHA_PNG:
7255 base = "alpha mode(PNG) ";
7256 bg = 4; /* should not be used */
7257 break;
7258 case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
7259 base = "alpha mode(Porter-Duff) ";
7260 bg = 4; /* should not be used */
7261 break;
7262 case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
7263 base = "alpha mode(Optimized) ";
7264 bg = 4; /* should not be used */
7265 break;
7266 case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
7267 base = "alpha mode(Broken) ";
7268 bg = 4; /* should not be used */
7269 break;
7270#endif
7271 }
7272
7273 /* Use random background values - the background is always presented in the
7274 * output space (8 or 16 bit components).
7275 */
7276 if (expand_16 || bit_depth == 16)
7277 {
7278 png_uint_32 r = random_32();
7279
7280 background.red = (png_uint_16)r;
7281 background.green = (png_uint_16)(r >> 16);
7282 r = random_32();
7283 background.blue = (png_uint_16)r;
7284 background.gray = (png_uint_16)(r >> 16);
7285 }
7286
7287 else /* 8 bit colors */
7288 {
7289 png_uint_32 r = random_32();
7290
7291 background.red = (png_byte)r;
7292 background.green = (png_byte)(r >> 8);
7293 background.blue = (png_byte)(r >> 16);
7294 background.gray = (png_byte)(r >> 24);
7295 }
7296
7297 background.index = 193; /* rgb(193,193,193) to detect errors */
7298 if (!(colour_type & PNG_COLOR_MASK_COLOR))
7299 {
7300 /* Grayscale input, we do not convert to RGB (TBD), so we must set the
7301 * background to gray - else libpng seems to fail.
7302 */
7303 background.red = background.green = background.blue = background.gray;
7304 }
7305
7306 pos = safecat(name, sizeof name, pos, base);
7307 pos = safecatd(name, sizeof name, pos, file_gamma, 3);
7308 if (do_background < ALPHA_MODE_OFFSET)
7309 {
7310 /* Include the background color and gamma in the name: */
7311 pos = safecat(name, sizeof name, pos, ",(");
7312 /* This assumes no expand gray->rgb - the current code won't handle that!
7313 */
7314 if (colour_type & PNG_COLOR_MASK_COLOR)
7315 {
7316 pos = safecatn(name, sizeof name, pos, background.red);
7317 pos = safecat(name, sizeof name, pos, ",");
John Bowler5441e182011-05-18 18:57:12 -05007318 pos = safecatn(name, sizeof name, pos, background.green);
John Bowlerd273ad22011-05-07 21:00:28 -05007319 pos = safecat(name, sizeof name, pos, ",");
7320 pos = safecatn(name, sizeof name, pos, background.blue);
7321 }
7322 else
7323 pos = safecatn(name, sizeof name, pos, background.gray);
7324 pos = safecat(name, sizeof name, pos, ")^");
7325 pos = safecatd(name, sizeof name, pos, bg, 3);
7326 }
7327 pos = safecat(name, sizeof name, pos, "->");
7328 pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
7329
John Bowler9994f252011-05-15 18:52:39 -05007330 gamma_test(pm, colour_type, bit_depth, palette_number, interlace_type,
John Bowler5441e182011-05-18 18:57:12 -05007331 file_gamma, screen_gamma, 0/*sBIT*/, 0, name, use_input_precision,
John Bowlerd273ad22011-05-07 21:00:28 -05007332 0/*strip 16*/, expand_16, do_background, &background, bg);
7333}
7334
7335
7336static void
John Bowler9994f252011-05-15 18:52:39 -05007337perform_gamma_composition_tests(png_modifier *pm, int do_background,
John Bowlerd273ad22011-05-07 21:00:28 -05007338 int expand_16)
7339{
7340 png_byte colour_type = 0;
7341 png_byte bit_depth = 0;
John Bowler9994f252011-05-15 18:52:39 -05007342 int palette_number = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05007343
John Bowler9994f252011-05-15 18:52:39 -05007344 /* Skip the non-alpha cases - there is no setting of a transparency colour at
7345 * present.
John Bowlerd273ad22011-05-07 21:00:28 -05007346 */
John Bowler9994f252011-05-15 18:52:39 -05007347 while (next_format(&colour_type, &bit_depth, &palette_number))
7348 if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0)
John Bowlerd273ad22011-05-07 21:00:28 -05007349 {
7350 unsigned int i, j;
7351
7352 /* Don't skip the i==j case here - it's relevant. */
7353 for (i=0; i<pm->ngammas; ++i) for (j=0; j<pm->ngammas; ++j)
7354 {
7355 /* use_input_precision must currently be false here because the checks
7356 * in gamma_component_validate switched on by use_input_precision do
7357 * *not* handle the composition being tested here.
7358 */
John Bowler9994f252011-05-15 18:52:39 -05007359 gamma_composition_test(pm, colour_type, bit_depth, palette_number,
7360 pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
John Bowler5441e182011-05-18 18:57:12 -05007361 pm->use_input_precision, do_background, expand_16);
John Bowlerd273ad22011-05-07 21:00:28 -05007362
7363 if (fail(pm))
7364 return;
7365 }
7366 }
7367}
7368#endif /* READ_BACKGROUND || READ_ALPHA_MODE */
7369
7370static void
7371init_gamma_errors(png_modifier *pm)
7372{
John Bowler1921e6d2011-05-16 20:57:54 -05007373 pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0;
7374 pm->error_color_8 = 0;
7375 pm->error_indexed = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05007376 pm->error_gray_16 = pm->error_color_16 = 0;
7377}
7378
7379static void
7380summarize_gamma_errors(png_modifier *pm, png_const_charp who, int low_bit_depth)
7381{
7382 if (who)
7383 printf("Gamma correction with %s:\n", who);
7384
7385 if (low_bit_depth)
7386 {
7387 printf(" 2 bit gray: %.5f\n", pm->error_gray_2);
7388 printf(" 4 bit gray: %.5f\n", pm->error_gray_4);
7389 printf(" 8 bit gray: %.5f\n", pm->error_gray_8);
7390 printf(" 8 bit color: %.5f\n", pm->error_color_8);
John Bowler1921e6d2011-05-16 20:57:54 -05007391 printf(" indexed: %.5f\n", pm->error_indexed);
John Bowlerd273ad22011-05-07 21:00:28 -05007392 }
7393
7394#ifdef DO_16BIT
7395 printf(" 16 bit gray: %.5f\n", pm->error_gray_16);
7396 printf(" 16 bit color: %.5f\n", pm->error_color_16);
7397#endif
7398}
7399
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007400static void
John Bowler9994f252011-05-15 18:52:39 -05007401perform_gamma_test(png_modifier *pm, int summary)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007402{
John Bowler5441e182011-05-18 18:57:12 -05007403 /*TODO: remove this*/
7404 /* Save certain values for the temporary overrides below. */
7405 unsigned int calculations_use_input_precision =
7406 pm->calculations_use_input_precision;
7407 double maxout8 = pm->maxout8;
7408
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007409 /* First some arbitrary no-transform tests: */
John Bowler9994f252011-05-15 18:52:39 -05007410 if (!pm->this.speed && pm->test_gamma_threshold)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007411 {
7412 perform_gamma_threshold_tests(pm);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007413
7414 if (fail(pm))
7415 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007416 }
7417
7418 /* Now some real transforms. */
John Bowlerf21a0d02011-01-23 23:55:19 -06007419 if (pm->test_gamma_transform)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007420 {
John Bowlerd273ad22011-05-07 21:00:28 -05007421 init_gamma_errors(pm);
John Bowler5441e182011-05-18 18:57:12 -05007422 /*TODO: remove this. Necessary because the currently libpng
7423 * implementation works in 8 bits:
7424 */
7425 if (pm->test_gamma_expand16)
7426 pm->calculations_use_input_precision = 1;
John Bowler9994f252011-05-15 18:52:39 -05007427 perform_gamma_transform_tests(pm);
John Bowler5441e182011-05-18 18:57:12 -05007428 if (!calculations_use_input_precision)
7429 pm->calculations_use_input_precision = 0;
Glenn Randers-Pehrsonf1cf9022010-12-07 14:40:33 -06007430
John Bowlerb54498e2010-12-08 16:26:21 -06007431 if (summary)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007432 {
John Bowlerb54498e2010-12-08 16:26:21 -06007433 printf("Gamma correction error summary\n\n");
7434 printf("The printed value is the maximum error in the pixel values\n");
7435 printf("calculated by the libpng gamma correction code. The error\n");
7436 printf("is calculated as the difference between the output pixel\n");
7437 printf("value (always an integer) and the ideal value from the\n");
7438 printf("libpng specification (typically not an integer).\n\n");
7439
7440 printf("Expect this value to be less than .5 for 8 bit formats,\n");
7441 printf("less than 1 for formats with fewer than 8 bits and a small\n");
7442 printf("number (typically less than 5) for the 16 bit formats.\n");
7443 printf("For performance reasons the value for 16 bit formats\n");
7444 printf("increases when the image file includes an sBIT chunk.\n\n");
7445
John Bowlerd273ad22011-05-07 21:00:28 -05007446 summarize_gamma_errors(pm, 0/*who*/, 1);
John Bowlerb54498e2010-12-08 16:26:21 -06007447 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007448 }
7449
John Bowlerb54498e2010-12-08 16:26:21 -06007450 /* The sbit tests produce much larger errors: */
John Bowlerf21a0d02011-01-23 23:55:19 -06007451 if (pm->test_gamma_sbit)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007452 {
John Bowlerd273ad22011-05-07 21:00:28 -05007453 init_gamma_errors(pm);
John Bowler9994f252011-05-15 18:52:39 -05007454 perform_gamma_sbit_tests(pm);
John Bowlerb54498e2010-12-08 16:26:21 -06007455
7456 if (summary)
John Bowlerd273ad22011-05-07 21:00:28 -05007457 summarize_gamma_errors(pm, "sBIT", pm->sbitlow < 8U);
John Bowlerb54498e2010-12-08 16:26:21 -06007458 }
7459
John Bowler9994f252011-05-15 18:52:39 -05007460#ifdef DO_16BIT /* Should be READ_16BIT_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06007461 if (pm->test_gamma_strip16)
John Bowlerb54498e2010-12-08 16:26:21 -06007462 {
7463 /* The 16 to 8 bit strip operations: */
John Bowlerd273ad22011-05-07 21:00:28 -05007464 init_gamma_errors(pm);
John Bowler9994f252011-05-15 18:52:39 -05007465 perform_gamma_strip16_tests(pm);
John Bowlerb54498e2010-12-08 16:26:21 -06007466
7467 if (summary)
7468 {
7469 printf("Gamma correction with 16 to 8 bit reduction:\n");
7470 printf(" 16 bit gray: %.5f\n", pm->error_gray_16);
7471 printf(" 16 bit color: %.5f\n", pm->error_color_16);
7472 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007473 }
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05007474#endif
John Bowlerd273ad22011-05-07 21:00:28 -05007475
7476#ifdef PNG_READ_BACKGROUND_SUPPORTED
7477 if (pm->test_gamma_background)
7478 {
7479 init_gamma_errors(pm);
7480
John Bowler5441e182011-05-18 18:57:12 -05007481 /*TODO: remove this. Necessary because the currently libpng
7482 * implementation works in 8 bits:
7483 */
7484 if (pm->test_gamma_expand16)
7485 {
7486 pm->calculations_use_input_precision = 1;
7487 pm->maxout8 = .499; /* because the 16 bit background is smashed */
7488 }
John Bowler9994f252011-05-15 18:52:39 -05007489 perform_gamma_composition_tests(pm, PNG_BACKGROUND_GAMMA_UNIQUE,
John Bowlerd273ad22011-05-07 21:00:28 -05007490 pm->test_gamma_expand16);
John Bowler5441e182011-05-18 18:57:12 -05007491 if (!calculations_use_input_precision)
7492 pm->calculations_use_input_precision = 0;
7493 pm->maxout8 = maxout8;
John Bowlerd273ad22011-05-07 21:00:28 -05007494
7495 if (summary)
7496 summarize_gamma_errors(pm, "background", 1);
7497 }
7498#endif
7499
7500#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
7501 if (pm->test_gamma_alpha_mode)
7502 {
7503 int do_background;
7504
7505 init_gamma_errors(pm);
7506
John Bowler5441e182011-05-18 18:57:12 -05007507 /*TODO: remove this. Necessary because the currently libpng
7508 * implementation works in 8 bits:
7509 */
7510 if (pm->test_gamma_expand16)
7511 pm->calculations_use_input_precision = 1;
John Bowlerd273ad22011-05-07 21:00:28 -05007512 for (do_background = ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD;
7513 do_background <= ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN && !fail(pm);
7514 ++do_background)
John Bowler9994f252011-05-15 18:52:39 -05007515 perform_gamma_composition_tests(pm, do_background,
John Bowlerd273ad22011-05-07 21:00:28 -05007516 pm->test_gamma_expand16);
John Bowler5441e182011-05-18 18:57:12 -05007517 if (!calculations_use_input_precision)
7518 pm->calculations_use_input_precision = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05007519
7520 if (summary)
7521 summarize_gamma_errors(pm, "alpha mode", 1);
7522 }
7523#endif
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007524}
John Bowler4a12f4a2011-04-17 18:34:22 -05007525#endif /* PNG_READ_GAMMA_SUPPORTED */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007526
John Bowler660c6e42010-12-19 06:22:23 -06007527/* INTERLACE MACRO VALIDATION */
7528/* This is copied verbatim from the specification, it is simply the pass
7529 * number in which each pixel in each 8x8 tile appears. The array must
7530 * be indexed adam7[y][x] and notice that the pass numbers are based at
7531 * 1, not 0 - the base libpng uses.
7532 */
7533static PNG_CONST
7534png_byte adam7[8][8] =
7535{
7536 { 1,6,4,6,2,6,4,6 },
7537 { 7,7,7,7,7,7,7,7 },
7538 { 5,6,5,6,5,6,5,6 },
7539 { 7,7,7,7,7,7,7,7 },
7540 { 3,6,4,6,3,6,4,6 },
7541 { 7,7,7,7,7,7,7,7 },
7542 { 5,6,5,6,5,6,5,6 },
7543 { 7,7,7,7,7,7,7,7 }
7544};
7545
7546/* This routine validates all the interlace support macros in png.h for
7547 * a variety of valid PNG widths and heights. It uses a number of similarly
7548 * named internal routines that feed off the above array.
7549 */
7550static png_uint_32
7551png_pass_start_row(int pass)
7552{
7553 int x, y;
7554 ++pass;
7555 for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
7556 return y;
7557 return 0xf;
7558}
7559
7560static png_uint_32
7561png_pass_start_col(int pass)
7562{
7563 int x, y;
7564 ++pass;
7565 for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
7566 return x;
7567 return 0xf;
7568}
7569
7570static int
7571png_pass_row_shift(int pass)
7572{
7573 int x, y, base=(-1), inc=8;
7574 ++pass;
7575 for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
7576 {
7577 if (base == (-1))
7578 base = y;
7579 else if (base == y)
7580 {}
7581 else if (inc == y-base)
7582 base=y;
7583 else if (inc == 8)
7584 inc = y-base, base=y;
7585 else if (inc != y-base)
7586 return 0xff; /* error - more than one 'inc' value! */
7587 }
7588
7589 if (base == (-1)) return 0xfe; /* error - no row in pass! */
7590
7591 /* The shift is always 1, 2 or 3 - no pass has all the rows! */
7592 switch (inc)
7593 {
7594case 2: return 1;
7595case 4: return 2;
7596case 8: return 3;
7597default: break;
7598 }
7599
7600 /* error - unrecognized 'inc' */
7601 return (inc << 8) + 0xfd;
7602}
7603
7604static int
7605png_pass_col_shift(int pass)
7606{
7607 int x, y, base=(-1), inc=8;
7608 ++pass;
7609 for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
7610 {
7611 if (base == (-1))
7612 base = x;
7613 else if (base == x)
7614 {}
7615 else if (inc == x-base)
7616 base=x;
7617 else if (inc == 8)
7618 inc = x-base, base=x;
7619 else if (inc != x-base)
7620 return 0xff; /* error - more than one 'inc' value! */
7621 }
7622
7623 if (base == (-1)) return 0xfe; /* error - no row in pass! */
7624
7625 /* The shift is always 1, 2 or 3 - no pass has all the rows! */
7626 switch (inc)
7627 {
7628case 1: return 0; /* pass 7 has all the columns */
7629case 2: return 1;
7630case 4: return 2;
7631case 8: return 3;
7632default: break;
7633 }
7634
7635 /* error - unrecognized 'inc' */
7636 return (inc << 8) + 0xfd;
7637}
7638
7639static png_uint_32
7640png_row_from_pass_row(png_uint_32 yIn, int pass)
7641{
7642 /* By examination of the array: */
7643 switch (pass)
7644 {
7645case 0: return yIn * 8;
7646case 1: return yIn * 8;
7647case 2: return yIn * 8 + 4;
7648case 3: return yIn * 4;
7649case 4: return yIn * 4 + 2;
7650case 5: return yIn * 2;
7651case 6: return yIn * 2 + 1;
7652default: break;
7653 }
7654
7655 return 0xff; /* bad pass number */
7656}
7657
7658static png_uint_32
7659png_col_from_pass_col(png_uint_32 xIn, int pass)
7660{
7661 /* By examination of the array: */
7662 switch (pass)
7663 {
7664case 0: return xIn * 8;
7665case 1: return xIn * 8 + 4;
7666case 2: return xIn * 4;
7667case 3: return xIn * 4 + 2;
7668case 4: return xIn * 2;
7669case 5: return xIn * 2 + 1;
7670case 6: return xIn;
7671default: break;
7672 }
7673
7674 return 0xff; /* bad pass number */
7675}
7676
7677static int
7678png_row_in_interlace_pass(png_uint_32 y, int pass)
7679{
7680 /* Is row 'y' in pass 'pass'? */
7681 int x;
7682 y &= 7;
7683 ++pass;
7684 for (x=0; x<8; ++x) if (adam7[y][x] == pass)
7685 return 1;
7686
7687 return 0;
7688}
7689
7690static int
7691png_col_in_interlace_pass(png_uint_32 x, int pass)
7692{
7693 /* Is column 'x' in pass 'pass'? */
7694 int y;
7695 x &= 7;
7696 ++pass;
7697 for (y=0; y<8; ++y) if (adam7[y][x] == pass)
7698 return 1;
7699
7700 return 0;
7701}
7702
7703static png_uint_32
7704png_pass_rows(png_uint_32 height, int pass)
7705{
7706 png_uint_32 tiles = height>>3;
7707 png_uint_32 rows = 0;
7708 unsigned int x, y;
7709
7710 height &= 7;
7711 ++pass;
7712 for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
7713 {
7714 rows += tiles;
7715 if (y < height) ++rows;
7716 break; /* i.e. break the 'x', column, loop. */
7717 }
7718
7719 return rows;
7720}
7721
7722static png_uint_32
7723png_pass_cols(png_uint_32 width, int pass)
7724{
7725 png_uint_32 tiles = width>>3;
7726 png_uint_32 cols = 0;
7727 unsigned int x, y;
7728
7729 width &= 7;
7730 ++pass;
7731 for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
7732 {
7733 cols += tiles;
7734 if (x < width) ++cols;
7735 break; /* i.e. break the 'y', row, loop. */
7736 }
7737
7738 return cols;
7739}
7740
7741static void
7742perform_interlace_macro_validation(void)
7743{
7744 /* The macros to validate, first those that depend only on pass:
7745 *
7746 * PNG_PASS_START_ROW(pass)
7747 * PNG_PASS_START_COL(pass)
7748 * PNG_PASS_ROW_SHIFT(pass)
7749 * PNG_PASS_COL_SHIFT(pass)
7750 */
7751 int pass;
7752
7753 for (pass=0; pass<7; ++pass)
7754 {
7755 png_uint_32 m, f, v;
7756
7757 m = PNG_PASS_START_ROW(pass);
7758 f = png_pass_start_row(pass);
7759 if (m != f)
7760 {
7761 fprintf(stderr, "PNG_PASS_START_ROW(%d) = %u != %x\n", pass, m, f);
7762 exit(1);
7763 }
7764
7765 m = PNG_PASS_START_COL(pass);
7766 f = png_pass_start_col(pass);
7767 if (m != f)
7768 {
7769 fprintf(stderr, "PNG_PASS_START_COL(%d) = %u != %x\n", pass, m, f);
7770 exit(1);
7771 }
7772
7773 m = PNG_PASS_ROW_SHIFT(pass);
7774 f = png_pass_row_shift(pass);
7775 if (m != f)
7776 {
7777 fprintf(stderr, "PNG_PASS_ROW_SHIFT(%d) = %u != %x\n", pass, m, f);
7778 exit(1);
7779 }
7780
7781 m = PNG_PASS_COL_SHIFT(pass);
7782 f = png_pass_col_shift(pass);
7783 if (m != f)
7784 {
7785 fprintf(stderr, "PNG_PASS_COL_SHIFT(%d) = %u != %x\n", pass, m, f);
7786 exit(1);
7787 }
7788
7789 /* Macros that depend on the image or sub-image height too:
7790 *
7791 * PNG_PASS_ROWS(height, pass)
7792 * PNG_PASS_COLS(width, pass)
7793 * PNG_ROW_FROM_PASS_ROW(yIn, pass)
7794 * PNG_COL_FROM_PASS_COL(xIn, pass)
7795 * PNG_ROW_IN_INTERLACE_PASS(y, pass)
7796 * PNG_COL_IN_INTERLACE_PASS(x, pass)
7797 */
7798 for (v=0;;)
7799 {
7800 /* First the base 0 stuff: */
7801 m = PNG_ROW_FROM_PASS_ROW(v, pass);
7802 f = png_row_from_pass_row(v, pass);
7803 if (m != f)
7804 {
7805 fprintf(stderr, "PNG_ROW_FROM_PASS_ROW(%u, %d) = %u != %x\n",
7806 v, pass, m, f);
7807 exit(1);
7808 }
7809
7810 m = PNG_COL_FROM_PASS_COL(v, pass);
7811 f = png_col_from_pass_col(v, pass);
7812 if (m != f)
7813 {
7814 fprintf(stderr, "PNG_COL_FROM_PASS_COL(%u, %d) = %u != %x\n",
7815 v, pass, m, f);
7816 exit(1);
7817 }
7818
7819 m = PNG_ROW_IN_INTERLACE_PASS(v, pass);
7820 f = png_row_in_interlace_pass(v, pass);
7821 if (m != f)
7822 {
7823 fprintf(stderr, "PNG_ROW_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
7824 v, pass, m, f);
7825 exit(1);
7826 }
7827
7828 m = PNG_COL_IN_INTERLACE_PASS(v, pass);
7829 f = png_col_in_interlace_pass(v, pass);
7830 if (m != f)
7831 {
7832 fprintf(stderr, "PNG_COL_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
7833 v, pass, m, f);
7834 exit(1);
7835 }
7836
7837 /* Then the base 1 stuff: */
7838 ++v;
7839 m = PNG_PASS_ROWS(v, pass);
7840 f = png_pass_rows(v, pass);
7841 if (m != f)
7842 {
7843 fprintf(stderr, "PNG_PASS_ROWS(%u, %d) = %u != %x\n",
7844 v, pass, m, f);
7845 exit(1);
7846 }
7847
7848 m = PNG_PASS_COLS(v, pass);
7849 f = png_pass_cols(v, pass);
7850 if (m != f)
7851 {
7852 fprintf(stderr, "PNG_PASS_COLS(%u, %d) = %u != %x\n",
7853 v, pass, m, f);
7854 exit(1);
7855 }
7856
7857 /* Move to the next v - the stepping algorithm starts skipping
7858 * values above 1024.
7859 */
7860 if (v > 1024)
7861 {
7862 if (v == PNG_UINT_31_MAX)
7863 break;
7864
7865 v = (v << 1) ^ v;
7866 if (v >= PNG_UINT_31_MAX)
7867 v = PNG_UINT_31_MAX-1;
7868 }
7869 }
7870 }
7871}
7872
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007873/* main program */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05007874int main(int argc, PNG_CONST char **argv)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007875{
John Bowler660c6e42010-12-19 06:22:23 -06007876 volatile int summary = 1; /* Print the error summary at the end */
John Bowler1921e6d2011-05-16 20:57:54 -05007877 volatile int memstats = 0; /* Print memory statistics at the end */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007878
7879 /* Create the given output file on success: */
7880 PNG_CONST char *volatile touch = NULL;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007881
7882 /* This is an array of standard gamma values (believe it or not I've seen
7883 * every one of these mentioned somewhere.)
7884 *
7885 * In the following list the most useful values are first!
7886 */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007887 static double
7888 gammas[]={2.2, 1.0, 2.2/1.45, 1.8, 1.5, 2.4, 2.5, 2.62, 2.9};
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007889
John Bowler1921e6d2011-05-16 20:57:54 -05007890 /* This records the command and arguments: */
7891 size_t cp = 0;
7892 char command[1024];
7893
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007894 png_modifier pm;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007895 context(&pm.this, fault);
7896
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007897 modifier_init(&pm);
7898
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05007899 /* Preallocate the image buffer, because we know how big it needs to be,
John Bowler9994f252011-05-15 18:52:39 -05007900 * note that, for testing purposes, it is deliberately mis-aligned by tag
7901 * bytes either side. All rows have an additional five bytes of padding for
7902 * overwrite checking.
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05007903 */
John Bowler9994f252011-05-15 18:52:39 -05007904 store_ensure_image(&pm.this, NULL, 2, TRANSFORM_ROWMAX, TRANSFORM_HEIGHTMAX);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05007905
John Bowler1921e6d2011-05-16 20:57:54 -05007906 /* Don't give argv[0], it's normally some horrible libtool string: */
7907 cp = safecat(command, sizeof command, cp, "pngvalid");
7908
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007909 /* Default to error on warning: */
7910 pm.this.treat_warnings_as_errors = 1;
7911
7912 /* Store the test gammas */
7913 pm.gammas = gammas;
John Bowlerafea7d12011-01-28 06:38:14 -06007914 pm.ngammas = 0; /* default to off */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007915 pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */
John Bowler5441e182011-05-18 18:57:12 -05007916 /* The following allows results to pass if they correspond to anything in the
7917 * transformed range [input-.5,input+.5]; this is is required because of the
7918 * way libpng treates the 16_TO_8 flag when building the gamma tables.
7919 *
7920 * TODO: review this
7921 */
7922 pm.use_input_precision_16to8 = 1U;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007923
Glenn Randers-Pehrsonf1cf9022010-12-07 14:40:33 -06007924 /* Some default values (set the behavior for 'make check' here).
7925 * These values simply control the maximum error permitted in the gamma
7926 * transformations. The practial limits for human perception are described
7927 * below (the setting for maxpc16), however for 8 bit encodings it isn't
7928 * possible to meet the accepted capabilities of human vision - i.e. 8 bit
Glenn Randers-Pehrson9f044c12010-12-07 14:59:43 -06007929 * images can never be good enough, regardless of encoding.
Glenn Randers-Pehrsonf1cf9022010-12-07 14:40:33 -06007930 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007931 pm.maxout8 = .1; /* Arithmetic error in *encoded* value */
7932 pm.maxabs8 = .00005; /* 1/20000 */
John Bowler5441e182011-05-18 18:57:12 -05007933 pm.maxcalc8 = .004; /* +/-1 in 8 bits for compose errors */
John Bowlerb54498e2010-12-08 16:26:21 -06007934 pm.maxpc8 = .499; /* I.e., .499% fractional error */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007935 pm.maxout16 = .499; /* Error in *encoded* value */
7936 pm.maxabs16 = .00005;/* 1/20000 */
John Bowler5441e182011-05-18 18:57:12 -05007937 pm.maxcalc16 =.000015;/* +/-1 in 16 bits for compose errors */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007938
7939 /* NOTE: this is a reasonable perceptual limit. We assume that humans can
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007940 * perceive light level differences of 1% over a 100:1 range, so we need to
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007941 * maintain 1 in 10000 accuracy (in linear light space), which is what the
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007942 * following guarantees. It also allows significantly higher errors at
7943 * higher 16 bit values, which is important for performance. The actual
7944 * maximum 16 bit error is about +/-1.9 in the fixed point implementation but
7945 * this is only allowed for values >38149 by the following:
7946 */
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06007947 pm.maxpc16 = .005; /* I.e., 1/200% - 1/20000 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007948
7949 /* Now parse the command line options. */
7950 while (--argc >= 1)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007951 {
John Bowler5441e182011-05-18 18:57:12 -05007952 int catmore = 0; /* Set if the argument has an argument. */
7953
John Bowler1921e6d2011-05-16 20:57:54 -05007954 /* Record each argument for posterity: */
7955 cp = safecat(command, sizeof command, cp, " ");
7956 cp = safecat(command, sizeof command, cp, *++argv);
7957
7958 if (strcmp(*argv, "-v") == 0)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007959 pm.this.verbose = 1;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007960
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007961 else if (strcmp(*argv, "-l") == 0)
7962 pm.log = 1;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007963
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007964 else if (strcmp(*argv, "-q") == 0)
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05007965 summary = pm.this.verbose = pm.log = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007966
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007967 else if (strcmp(*argv, "-w") == 0)
7968 pm.this.treat_warnings_as_errors = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007969
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05007970 else if (strcmp(*argv, "--speed") == 0)
John Bowlerb54498e2010-12-08 16:26:21 -06007971 pm.this.speed = 1, pm.ngammas = (sizeof gammas)/(sizeof gammas[0]),
John Bowler1921e6d2011-05-16 20:57:54 -05007972 pm.test_standard = 0, summary = 0;
7973
7974 else if (strcmp(*argv, "--memory") == 0)
7975 memstats = 1;
John Bowlerb54498e2010-12-08 16:26:21 -06007976
John Bowler660c6e42010-12-19 06:22:23 -06007977 else if (strcmp(*argv, "--size") == 0)
7978 pm.test_size = 1;
7979
John Bowlerafea7d12011-01-28 06:38:14 -06007980 else if (strcmp(*argv, "--nosize") == 0)
7981 pm.test_size = 0;
7982
7983 else if (strcmp(*argv, "--standard") == 0)
7984 pm.test_standard = 1;
7985
John Bowlerb54498e2010-12-08 16:26:21 -06007986 else if (strcmp(*argv, "--nostandard") == 0)
7987 pm.test_standard = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007988
John Bowlerafea7d12011-01-28 06:38:14 -06007989 else if (strcmp(*argv, "--transform") == 0)
7990 pm.test_transform = 1;
7991
John Bowlerf21a0d02011-01-23 23:55:19 -06007992 else if (strcmp(*argv, "--notransform") == 0)
7993 pm.test_transform = 0;
7994
John Bowler4a12f4a2011-04-17 18:34:22 -05007995#ifdef PNG_READ_TRANSFORMS_SUPPORTED
John Bowlerafea7d12011-01-28 06:38:14 -06007996 else if (strncmp(*argv, "--transform-disable=",
7997 sizeof "--transform-disable") == 0)
7998 {
7999 pm.test_transform = 1;
8000 transform_disable(*argv + sizeof "--transform-disable");
8001 }
8002
8003 else if (strncmp(*argv, "--transform-enable=",
8004 sizeof "--transform-enable") == 0)
8005 {
8006 pm.test_transform = 1;
8007 transform_enable(*argv + sizeof "--transform-enable");
8008 }
John Bowler4a12f4a2011-04-17 18:34:22 -05008009#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
John Bowlerafea7d12011-01-28 06:38:14 -06008010
8011 else if (strcmp(*argv, "--gamma") == 0)
8012 {
8013 /* Just do two gamma tests here (2.2 and linear) for speed: */
8014 pm.ngammas = 2U;
8015 pm.test_gamma_threshold = 1;
8016 pm.test_gamma_transform = 1;
8017 pm.test_gamma_sbit = 1;
8018 pm.test_gamma_strip16 = 1;
John Bowlerd273ad22011-05-07 21:00:28 -05008019 pm.test_gamma_background = 1;
8020 pm.test_gamma_alpha_mode = 1;
John Bowlerafea7d12011-01-28 06:38:14 -06008021 }
8022
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05008023 else if (strcmp(*argv, "--nogamma") == 0)
8024 pm.ngammas = 0;
8025
John Bowlerafea7d12011-01-28 06:38:14 -06008026 else if (strcmp(*argv, "--gamma-threshold") == 0)
8027 pm.ngammas = 2U, pm.test_gamma_threshold = 1;
8028
John Bowlerb54498e2010-12-08 16:26:21 -06008029 else if (strcmp(*argv, "--nogamma-threshold") == 0)
John Bowlerf21a0d02011-01-23 23:55:19 -06008030 pm.test_gamma_threshold = 0;
John Bowlerb54498e2010-12-08 16:26:21 -06008031
John Bowlerafea7d12011-01-28 06:38:14 -06008032 else if (strcmp(*argv, "--gamma-transform") == 0)
8033 pm.ngammas = 2U, pm.test_gamma_transform = 1;
8034
John Bowlerb54498e2010-12-08 16:26:21 -06008035 else if (strcmp(*argv, "--nogamma-transform") == 0)
John Bowlerf21a0d02011-01-23 23:55:19 -06008036 pm.test_gamma_transform = 0;
John Bowlerb54498e2010-12-08 16:26:21 -06008037
John Bowlerafea7d12011-01-28 06:38:14 -06008038 else if (strcmp(*argv, "--gamma-sbit") == 0)
8039 pm.ngammas = 2U, pm.test_gamma_sbit = 1;
8040
John Bowlerb54498e2010-12-08 16:26:21 -06008041 else if (strcmp(*argv, "--nogamma-sbit") == 0)
John Bowlerf21a0d02011-01-23 23:55:19 -06008042 pm.test_gamma_sbit = 0;
John Bowlerb54498e2010-12-08 16:26:21 -06008043
John Bowlerafea7d12011-01-28 06:38:14 -06008044 else if (strcmp(*argv, "--gamma-16-to-8") == 0)
8045 pm.ngammas = 2U, pm.test_gamma_strip16 = 1;
8046
John Bowlerb54498e2010-12-08 16:26:21 -06008047 else if (strcmp(*argv, "--nogamma-16-to-8") == 0)
John Bowlerf21a0d02011-01-23 23:55:19 -06008048 pm.test_gamma_strip16 = 0;
John Bowlerb54498e2010-12-08 16:26:21 -06008049
John Bowlerd273ad22011-05-07 21:00:28 -05008050 else if (strcmp(*argv, "--gamma-background") == 0)
8051 pm.ngammas = 2U, pm.test_gamma_background = 1;
8052
8053 else if (strcmp(*argv, "--nogamma-background") == 0)
8054 pm.test_gamma_background = 0;
8055
8056 else if (strcmp(*argv, "--gamma-alpha-mode") == 0)
8057 pm.ngammas = 2U, pm.test_gamma_alpha_mode = 1;
8058
8059 else if (strcmp(*argv, "--nogamma-alpha-mode") == 0)
8060 pm.test_gamma_alpha_mode = 0;
8061
8062 else if (strcmp(*argv, "--expand16") == 0)
8063 pm.test_gamma_expand16 = 1;
8064
8065 else if (strcmp(*argv, "--noexpand16") == 0)
8066 pm.test_gamma_expand16 = 0;
8067
8068 else if (strcmp(*argv, "--more-gammas") == 0)
8069 pm.ngammas = 3U;
8070
John Bowlerafea7d12011-01-28 06:38:14 -06008071 else if (strcmp(*argv, "--all-gammas") == 0)
8072 pm.ngammas = (sizeof gammas)/(sizeof gammas[0]);
8073
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05008074 else if (strcmp(*argv, "--progressive-read") == 0)
8075 pm.this.progressive = 1;
8076
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05008077 else if (strcmp(*argv, "--interlace") == 0)
8078 pm.interlace_type = PNG_INTERLACE_ADAM7;
8079
John Bowlerd273ad22011-05-07 21:00:28 -05008080 else if (strcmp(*argv, "--use-input-precision") == 0)
8081 pm.use_input_precision = 1;
8082
John Bowler5441e182011-05-18 18:57:12 -05008083 else if (strcmp(*argv, "--calculations-use-input-precision") == 0)
8084 pm.calculations_use_input_precision = 1;
John Bowlerd273ad22011-05-07 21:00:28 -05008085
John Bowler5441e182011-05-18 18:57:12 -05008086 else if (strcmp(*argv, "--assume-16-bit-calculations") == 0)
8087 pm.assume_16_bit_calculations = 1;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008088
John Bowler5441e182011-05-18 18:57:12 -05008089 else if (strcmp(*argv, "--calculations-follow-bit-depth") == 0)
8090 pm.calculations_use_input_precision =
8091 pm.assume_16_bit_calculations = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008092
John Bowler5441e182011-05-18 18:57:12 -05008093 else if (argc > 1 && strcmp(*argv, "--sbitlow") == 0)
8094 --argc, pm.sbitlow = (png_byte)atoi(*++argv), catmore = 1;
8095
8096 else if (argc > 1 && strcmp(*argv, "--touch") == 0)
8097 --argc, touch = *++argv, catmore = 1;
8098
8099 else if (argc > 1 && strncmp(*argv, "--max", 5) == 0)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008100 {
8101 --argc;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008102
John Bowlerd273ad22011-05-07 21:00:28 -05008103 if (strcmp(5+*argv, "abs8") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008104 pm.maxabs8 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008105
John Bowlerd273ad22011-05-07 21:00:28 -05008106 else if (strcmp(5+*argv, "abs16") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008107 pm.maxabs16 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008108
John Bowlerd273ad22011-05-07 21:00:28 -05008109 else if (strcmp(5+*argv, "calc8") == 0)
8110 pm.maxcalc8 = atof(*++argv);
8111
8112 else if (strcmp(5+*argv, "calc16") == 0)
8113 pm.maxcalc16 = atof(*++argv);
8114
8115 else if (strcmp(5+*argv, "out8") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008116 pm.maxout8 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008117
John Bowlerd273ad22011-05-07 21:00:28 -05008118 else if (strcmp(5+*argv, "out16") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008119 pm.maxout16 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008120
John Bowlerd273ad22011-05-07 21:00:28 -05008121 else if (strcmp(5+*argv, "pc8") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008122 pm.maxpc8 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008123
John Bowlerd273ad22011-05-07 21:00:28 -05008124 else if (strcmp(5+*argv, "pc16") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008125 pm.maxpc16 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008126
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008127 else
8128 {
8129 fprintf(stderr, "pngvalid: %s: unknown 'max' option\n", *argv);
8130 exit(1);
8131 }
John Bowler5441e182011-05-18 18:57:12 -05008132
8133 catmore = 1;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008134 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008135
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008136 else
8137 {
8138 fprintf(stderr, "pngvalid: %s: unknown argument\n", *argv);
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008139 exit(1);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008140 }
John Bowler5441e182011-05-18 18:57:12 -05008141
8142 if (catmore) /* consumed an extra *argv */
8143 {
8144 cp = safecat(command, sizeof command, cp, " ");
8145 cp = safecat(command, sizeof command, cp, *argv);
8146 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008147 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008148
John Bowlerafea7d12011-01-28 06:38:14 -06008149 /* If pngvalid is run with no arguments default to a reasonable set of the
8150 * tests.
8151 */
8152 if (pm.test_standard == 0 && pm.test_size == 0 && pm.test_transform == 0 &&
8153 pm.ngammas == 0)
8154 {
John Bowlerd273ad22011-05-07 21:00:28 -05008155 /* Make this do all the tests done in the test shell scripts with the same
8156 * parameters, where possible. The limitation is that all the progressive
8157 * read and interlace stuff has to be done in separate runs, so only the
8158 * basic 'standard' and 'size' tests are done.
8159 */
John Bowlerafea7d12011-01-28 06:38:14 -06008160 pm.test_standard = 1;
8161 pm.test_size = 1;
8162 pm.test_transform = 1;
John Bowlerd273ad22011-05-07 21:00:28 -05008163 pm.ngammas = 2U;
John Bowlerafea7d12011-01-28 06:38:14 -06008164 }
8165
8166 if (pm.ngammas > 0 &&
8167 pm.test_gamma_threshold == 0 && pm.test_gamma_transform == 0 &&
John Bowlerd273ad22011-05-07 21:00:28 -05008168 pm.test_gamma_sbit == 0 && pm.test_gamma_strip16 == 0 &&
8169 pm.test_gamma_background == 0 && pm.test_gamma_alpha_mode == 0)
John Bowlerafea7d12011-01-28 06:38:14 -06008170 {
8171 pm.test_gamma_threshold = 1;
8172 pm.test_gamma_transform = 1;
8173 pm.test_gamma_sbit = 1;
8174 pm.test_gamma_strip16 = 1;
John Bowlerd273ad22011-05-07 21:00:28 -05008175 pm.test_gamma_background = 1;
8176 pm.test_gamma_alpha_mode = 1;
John Bowlerafea7d12011-01-28 06:38:14 -06008177 }
Glenn Randers-Pehrsonc36bb792011-02-12 09:49:07 -06008178
John Bowlerafea7d12011-01-28 06:38:14 -06008179 else if (pm.ngammas == 0)
8180 {
8181 /* Nothing to test so turn everything off: */
8182 pm.test_gamma_threshold = 0;
8183 pm.test_gamma_transform = 0;
8184 pm.test_gamma_sbit = 0;
8185 pm.test_gamma_strip16 = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05008186 pm.test_gamma_background = 0;
8187 pm.test_gamma_alpha_mode = 0;
John Bowlerafea7d12011-01-28 06:38:14 -06008188 }
8189
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008190 Try
8191 {
8192 /* Make useful base images */
John Bowler660c6e42010-12-19 06:22:23 -06008193 make_transform_images(&pm.this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008194
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008195 /* Perform the standard and gamma tests. */
John Bowlerb54498e2010-12-08 16:26:21 -06008196 if (pm.test_standard)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008197 {
John Bowler660c6e42010-12-19 06:22:23 -06008198 perform_interlace_macro_validation();
John Bowler88b77cc2011-05-05 06:49:55 -05008199 perform_formatting_test(&pm.this);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008200 perform_standard_test(&pm);
8201 perform_error_test(&pm);
8202 }
8203
John Bowler660c6e42010-12-19 06:22:23 -06008204 /* Various oddly sized images: */
8205 if (pm.test_size)
8206 {
8207 make_size_images(&pm.this);
8208 perform_size_test(&pm);
8209 }
8210
John Bowler4a12f4a2011-04-17 18:34:22 -05008211#ifdef PNG_READ_TRANSFORMS_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06008212 /* Combinatorial transforms: */
8213 if (pm.test_transform)
8214 perform_transform_test(&pm);
John Bowler4a12f4a2011-04-17 18:34:22 -05008215#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06008216
John Bowler4a12f4a2011-04-17 18:34:22 -05008217#ifdef PNG_READ_GAMMA_SUPPORTED
John Bowlerb54498e2010-12-08 16:26:21 -06008218 if (pm.ngammas > 0)
John Bowler1921e6d2011-05-16 20:57:54 -05008219 perform_gamma_test(&pm, summary);
John Bowler4a12f4a2011-04-17 18:34:22 -05008220#endif
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008221 }
8222
8223 Catch(fault)
8224 {
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05008225 fprintf(stderr, "pngvalid: test aborted (probably failed in cleanup)\n");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05008226 if (!pm.this.verbose)
8227 {
8228 if (pm.this.error[0] != 0)
8229 fprintf(stderr, "pngvalid: first error: %s\n", pm.this.error);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06008230
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05008231 fprintf(stderr, "pngvalid: run with -v to see what happened\n");
8232 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008233 exit(1);
8234 }
8235
John Bowler1921e6d2011-05-16 20:57:54 -05008236 if (summary)
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05008237 {
John Bowler5441e182011-05-18 18:57:12 -05008238 printf("%s: %s (%s point arithmetic)\n",
John Bowler1921e6d2011-05-16 20:57:54 -05008239 (pm.this.nerrors || (pm.this.treat_warnings_as_errors &&
8240 pm.this.nwarnings)) ? "FAIL" : "PASS",
8241 command,
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008242#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || PNG_LIBPNG_VER < 10500
John Bowler5441e182011-05-18 18:57:12 -05008243 "floating"
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008244#else
John Bowler5441e182011-05-18 18:57:12 -05008245 "fixed"
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008246#endif
John Bowler5441e182011-05-18 18:57:12 -05008247 );
John Bowler1921e6d2011-05-16 20:57:54 -05008248 }
8249
8250 if (memstats)
8251 {
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05008252 printf("Allocated memory statistics (in bytes):\n"
Glenn Randers-Pehrson9a75d992010-10-08 16:27:14 -05008253 "\tread %lu maximum single, %lu peak, %lu total\n"
8254 "\twrite %lu maximum single, %lu peak, %lu total\n",
8255 (unsigned long)pm.this.read_memory_pool.max_max,
8256 (unsigned long)pm.this.read_memory_pool.max_limit,
8257 (unsigned long)pm.this.read_memory_pool.max_total,
8258 (unsigned long)pm.this.write_memory_pool.max_max,
8259 (unsigned long)pm.this.write_memory_pool.max_limit,
8260 (unsigned long)pm.this.write_memory_pool.max_total);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05008261 }
8262
8263 /* Do this here to provoke memory corruption errors in memory not directly
8264 * allocated by libpng - not a complete test, but better than nothing.
8265 */
8266 store_delete(&pm.this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008267
8268 /* Error exit if there are any errors, and maybe if there are any
8269 * warnings.
8270 */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05008271 if (pm.this.nerrors || (pm.this.treat_warnings_as_errors &&
8272 pm.this.nwarnings))
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008273 {
8274 if (!pm.this.verbose)
8275 fprintf(stderr, "pngvalid: %s\n", pm.this.error);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008276
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008277 fprintf(stderr, "pngvalid: %d errors, %d warnings\n", pm.this.nerrors,
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008278 pm.this.nwarnings);
8279
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008280 exit(1);
8281 }
8282
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05008283 /* Success case. */
8284 if (touch != NULL)
8285 {
8286 FILE *fsuccess = fopen(touch, "wt");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008287
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05008288 if (fsuccess != NULL)
8289 {
8290 int error = 0;
8291 fprintf(fsuccess, "PNG validation succeeded\n");
8292 fflush(fsuccess);
8293 error = ferror(fsuccess);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008294
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05008295 if (fclose(fsuccess) || error)
8296 {
8297 fprintf(stderr, "%s: write failed\n", touch);
8298 exit(1);
8299 }
8300 }
8301 }
8302
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008303 return 0;
8304}