blob: 8a7b21614ea8d8105fc979bc45fa48e428010e27 [file] [log] [blame]
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001
2/* pngvalid.c - validate libpng by constructing then reading png files.
3 *
John Bowler550bab02011-06-14 06:17:26 -05004 * Last changed in libpng 1.5.4 [(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 Bowler6f55ee22011-06-11 07:28:06 -0500214 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 Bowler6f55ee22011-06-11 07:28:06 -0500218 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
John Bowler6f55ee22011-06-11 07:28:06 -0500729/* Verbose output to the error stream: */
730static void
731store_verbose(png_store *ps, png_structp pp, png_const_charp prefix,
732 png_const_charp message)
733{
734 char buffer[512];
735
736 if (prefix)
737 fputs(prefix, stderr);
738
739 (void)store_message(ps, pp, buffer, sizeof buffer, 0, message);
740 fputs(buffer, stderr);
741 fputc('\n', stderr);
742}
743
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500744/* Log an error or warning - the relevant count is always incremented. */
745static void
746store_log(png_store* ps, png_structp pp, png_const_charp message, int is_error)
747{
748 /* The warning is copied to the error buffer if there are no errors and it is
749 * the first warning. The error is copied to the error buffer if it is the
750 * first error (overwriting any prior warnings).
751 */
752 if (is_error ? (ps->nerrors)++ == 0 :
753 (ps->nwarnings)++ == 0 && ps->nerrors == 0)
754 store_message(ps, pp, ps->error, sizeof ps->error, 0, message);
755
756 if (ps->verbose)
John Bowler6f55ee22011-06-11 07:28:06 -0500757 store_verbose(ps, pp, is_error ? "error: " : "warning: ", message);
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500758}
759
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500760/* Functions to use as PNG callbacks. */
761static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500762store_error(png_structp pp, png_const_charp message) /* PNG_NORETURN */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500763{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500764 png_store *ps = png_get_error_ptr(pp);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500765
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500766 if (!ps->expect_error)
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600767 store_log(ps, pp, message, 1 /* error */);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500768
769 /* And finally throw an exception. */
770 {
771 struct exception_context *the_exception_context = &ps->exception_context;
772 Throw ps;
773 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500774}
775
776static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500777store_warning(png_structp pp, png_const_charp message)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500778{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500779 png_store *ps = png_get_error_ptr(pp);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500780
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500781 if (!ps->expect_warning)
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600782 store_log(ps, pp, message, 0 /* warning */);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500783 else
784 ps->saw_warning = 1;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500785}
786
John Bowler9994f252011-05-15 18:52:39 -0500787/* These somewhat odd functions are used when reading an image to ensure that
788 * the buffer is big enough, the png_structp is for errors.
789 */
790/* Return a single row from the correct image. */
791static png_bytep
792store_image_row(PNG_CONST png_store* ps, png_structp pp, int nImage,
793 png_uint_32 y)
794{
795 png_size_t coffset = (nImage * ps->image_h + y) * (ps->cb_row + 5) + 2;
796
797 if (ps->image == NULL)
798 png_error(pp, "no allocated image");
799
800 if (coffset + ps->cb_row + 3 > ps->cb_image)
801 png_error(pp, "image too small");
802
803 return ps->image + coffset;
804}
805
806static void
807store_image_free(png_store *ps, png_structp pp)
808{
809 if (ps->image != NULL)
810 {
811 png_bytep image = ps->image;
812
813 if (image[-1] != 0xed || image[ps->cb_image] != 0xfe)
814 {
815 if (pp != NULL)
816 png_error(pp, "png_store image overwrite (1)");
817 else
818 store_log(ps, NULL, "png_store image overwrite (2)", 1);
819 }
820
821 ps->image = NULL;
822 ps->cb_image = 0;
823 --image;
824 free(image);
825 }
826}
827
828static void
829store_ensure_image(png_store *ps, png_structp pp, int nImages, png_size_t cbRow,
830 png_uint_32 cRows)
831{
832 png_size_t cb = nImages * cRows * (cbRow + 5);
833
834 if (ps->cb_image < cb)
835 {
836 png_bytep image;
837
838 store_image_free(ps, pp);
839
840 /* The buffer is deliberately mis-aligned. */
841 image = malloc(cb+2);
842 if (image == NULL)
843 {
844 /* Called from the startup - ignore the error for the moment. */
845 if (pp == NULL)
846 return;
847
848 png_error(pp, "OOM allocating image buffer");
849 }
850
851 /* These magic tags are used to detect overwrites above. */
852 ++image;
853 image[-1] = 0xed;
854 image[cb] = 0xfe;
855
856 ps->image = image;
857 ps->cb_image = cb;
858 }
859
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -0500860 /* We have an adequate sized image; lay out the rows. There are 2 bytes at
John Bowler6f55ee22011-06-11 07:28:06 -0500861 * the start and three at the end of each (this ensures that the row
862 * alignment starts out odd - 2+1 and changes for larger images on each row.)
John Bowler9994f252011-05-15 18:52:39 -0500863 */
864 ps->cb_row = cbRow;
865 ps->image_h = cRows;
866
867 /* For error checking, the whole buffer is set to '1' - this matches what
868 * happens with the 'size' test images on write and also matches the unused
869 * bits in the test rows.
870 */
871 memset(ps->image, 0xff, cb);
872
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -0500873 /* Then put in the marks. */
John Bowler9994f252011-05-15 18:52:39 -0500874 while (--nImages >= 0)
875 {
876 png_uint_32 y;
877
878 for (y=0; y<cRows; ++y)
879 {
880 png_bytep row = store_image_row(ps, pp, nImages, y);
881
882 /* The markers: */
883 row[-2] = 190;
884 row[-1] = 239;
885 row[cbRow] = 222;
886 row[cbRow+1] = 173;
887 row[cbRow+2] = 17;
888 }
889 }
890}
891
892static void
893store_image_check(PNG_CONST png_store* ps, png_structp pp, int iImage)
894{
895 png_const_bytep image = ps->image;
896
897 if (image[-1] != 0xed || image[ps->cb_image] != 0xfe)
898 png_error(pp, "image overwrite");
899 else
900 {
901 png_size_t cbRow = ps->cb_row;
902 png_uint_32 rows = ps->image_h;
903
904 image += iImage * (cbRow+5) * ps->image_h;
905
906 image += 2; /* skip image first row markers */
907
908 while (rows-- > 0)
909 {
910 if (image[-2] != 190 || image[-1] != 239)
911 png_error(pp, "row start overwritten");
912
913 if (image[cbRow] != 222 || image[cbRow+1] != 173 ||
914 image[cbRow+2] != 17)
915 png_error(pp, "row end overwritten");
916
917 image += cbRow+5;
918 }
919 }
920}
921
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500922static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500923store_write(png_structp pp, png_bytep pb, png_size_t st)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500924{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500925 png_store *ps = png_get_io_ptr(pp);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500926
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500927 if (ps->pwrite != pp)
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500928 png_error(pp, "store state damaged");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500929
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500930 while (st > 0)
931 {
932 size_t cb;
933
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500934 if (ps->writepos >= STORE_BUFFER_SIZE)
935 store_storenew(ps);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500936
937 cb = st;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500938
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500939 if (cb > STORE_BUFFER_SIZE - ps->writepos)
940 cb = STORE_BUFFER_SIZE - ps->writepos;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500941
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500942 memcpy(ps->new.buffer + ps->writepos, pb, cb);
943 pb += cb;
944 st -= cb;
945 ps->writepos += cb;
946 }
947}
948
949static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500950store_flush(png_structp pp)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500951{
Glenn Randers-Pehrsond546f432010-12-04 20:41:36 -0600952 UNUSED(pp) /*DOES NOTHING*/
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500953}
954
955static size_t
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500956store_read_buffer_size(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500957{
958 /* Return the bytes available for read in the current buffer. */
959 if (ps->next != &ps->current->data)
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500960 return STORE_BUFFER_SIZE;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500961
962 return ps->current->datacount;
963}
964
John Bowler4a12f4a2011-04-17 18:34:22 -0500965#ifdef PNG_READ_TRANSFORMS_SUPPORTED
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500966/* Return total bytes available for read. */
967static size_t
968store_read_buffer_avail(png_store *ps)
969{
970 if (ps->current != NULL && ps->next != NULL)
971 {
972 png_store_buffer *next = &ps->current->data;
973 size_t cbAvail = ps->current->datacount;
974
975 while (next != ps->next && next != NULL)
976 {
977 next = next->prev;
978 cbAvail += STORE_BUFFER_SIZE;
979 }
980
981 if (next != ps->next)
982 png_error(ps->pread, "buffer read error");
983
984 if (cbAvail > ps->readpos)
985 return cbAvail - ps->readpos;
986 }
987
988 return 0;
989}
John Bowler4a12f4a2011-04-17 18:34:22 -0500990#endif
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500991
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500992static int
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500993store_read_buffer_next(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500994{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500995 png_store_buffer *pbOld = ps->next;
996 png_store_buffer *pbNew = &ps->current->data;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500997 if (pbOld != pbNew)
998 {
999 while (pbNew != NULL && pbNew->prev != pbOld)
1000 pbNew = pbNew->prev;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001001
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001002 if (pbNew != NULL)
1003 {
1004 ps->next = pbNew;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001005 ps->readpos = 0;
1006 return 1;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001007 }
1008
1009 png_error(ps->pread, "buffer lost");
1010 }
1011
1012 return 0; /* EOF or error */
1013}
1014
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001015/* Need separate implementation and callback to allow use of the same code
1016 * during progressive read, where the io_ptr is set internally by libpng.
1017 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001018static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001019store_read_imp(png_store *ps, png_bytep pb, png_size_t st)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001020{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001021 if (ps->current == NULL || ps->next == NULL)
1022 png_error(ps->pread, "store state damaged");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001023
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001024 while (st > 0)
1025 {
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001026 size_t cbAvail = store_read_buffer_size(ps) - ps->readpos;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001027
1028 if (cbAvail > 0)
1029 {
1030 if (cbAvail > st) cbAvail = st;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001031 memcpy(pb, ps->next->buffer + ps->readpos, cbAvail);
1032 st -= cbAvail;
1033 pb += cbAvail;
1034 ps->readpos += cbAvail;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001035 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001036
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001037 else if (!store_read_buffer_next(ps))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001038 png_error(ps->pread, "read beyond end of file");
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001039 }
1040}
1041
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001042static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001043store_read(png_structp pp, png_bytep pb, png_size_t st)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001044{
1045 png_store *ps = png_get_io_ptr(pp);
1046
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001047 if (ps == NULL || ps->pread != pp)
1048 png_error(pp, "bad store read call");
1049
1050 store_read_imp(ps, pb, st);
1051}
1052
1053static void
1054store_progressive_read(png_store *ps, png_structp pp, png_infop pi)
1055{
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001056 /* Notice that a call to store_read will cause this function to fail because
1057 * readpos will be set.
1058 */
1059 if (ps->pread != pp || ps->current == NULL || ps->next == NULL)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001060 png_error(pp, "store state damaged (progressive)");
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001061
1062 do
1063 {
1064 if (ps->readpos != 0)
1065 png_error(pp, "store_read called during progressive read");
1066
1067 png_process_data(pp, pi, ps->next->buffer, store_read_buffer_size(ps));
1068 }
1069 while (store_read_buffer_next(ps));
1070}
1071
John Bowler9994f252011-05-15 18:52:39 -05001072/* The caller must fill this in: */
1073static store_palette_entry *
1074store_write_palette(png_store *ps, int npalette)
1075{
1076 if (ps->pwrite == NULL)
1077 store_log(ps, NULL, "attempt to write palette without write stream", 1);
1078
1079 if (ps->palette != NULL)
1080 png_error(ps->pwrite, "multiple store_write_palette calls");
1081
1082 /* This function can only return NULL if called with '0'! */
1083 if (npalette > 0)
1084 {
1085 ps->palette = malloc(npalette * sizeof *ps->palette);
1086
1087 if (ps->palette == NULL)
1088 png_error(ps->pwrite, "store new palette: OOM");
1089
1090 ps->npalette = npalette;
1091 }
1092
1093 return ps->palette;
1094}
1095
1096static store_palette_entry *
1097store_current_palette(png_store *ps, int *npalette)
1098{
1099 /* This is an internal error (the call has been made outside a read
1100 * operation.)
1101 */
1102 if (ps->current == NULL)
1103 store_log(ps, ps->pread, "no current stream for palette", 1);
1104
1105 /* The result may be null if there is no palette. */
1106 *npalette = ps->current->npalette;
1107 return ps->current->palette;
1108}
1109
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001110/***************************** MEMORY MANAGEMENT*** ***************************/
1111/* A store_memory is simply the header for an allocated block of memory. The
1112 * pointer returned to libpng is just after the end of the header block, the
1113 * allocated memory is followed by a second copy of the 'mark'.
1114 */
1115typedef struct store_memory
1116{
1117 store_pool *pool; /* Originating pool */
1118 struct store_memory *next; /* Singly linked list */
1119 png_alloc_size_t size; /* Size of memory allocated */
1120 png_byte mark[4]; /* ID marker */
1121} store_memory;
1122
1123/* Handle a fatal error in memory allocation. This calls png_error if the
1124 * libpng struct is non-NULL, else it outputs a message and returns. This means
1125 * that a memory problem while libpng is running will abort (png_error) the
1126 * handling of particular file while one in cleanup (after the destroy of the
1127 * struct has returned) will simply keep going and free (or attempt to free)
1128 * all the memory.
1129 */
1130static void
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001131store_pool_error(png_store *ps, png_structp pp, PNG_CONST char *msg)
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001132{
1133 if (pp != NULL)
1134 png_error(pp, msg);
1135
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001136 /* Else we have to do it ourselves. png_error eventually calls store_log,
1137 * above. store_log accepts a NULL png_structp - it just changes what gets
1138 * output by store_message.
1139 */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001140 store_log(ps, pp, msg, 1 /* error */);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001141}
1142
1143static void
1144store_memory_free(png_structp pp, store_pool *pool, store_memory *memory)
1145{
1146 /* Note that pp may be NULL (see store_pool_delete below), the caller has
1147 * found 'memory' in pool->list *and* unlinked this entry, so this is a valid
1148 * pointer (for sure), but the contents may have been trashed.
1149 */
1150 if (memory->pool != pool)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001151 store_pool_error(pool->store, pp, "memory corrupted (pool)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001152
1153 else if (memcmp(memory->mark, pool->mark, sizeof memory->mark) != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001154 store_pool_error(pool->store, pp, "memory corrupted (start)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001155
1156 /* It should be safe to read the size field now. */
1157 else
1158 {
1159 png_alloc_size_t cb = memory->size;
1160
1161 if (cb > pool->max)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001162 store_pool_error(pool->store, pp, "memory corrupted (size)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001163
1164 else if (memcmp((png_bytep)(memory+1)+cb, pool->mark, sizeof pool->mark)
1165 != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001166 store_pool_error(pool->store, pp, "memory corrupted (end)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001167
1168 /* Finally give the library a chance to find problems too: */
1169 else
1170 {
1171 pool->current -= cb;
1172 free(memory);
1173 }
1174 }
1175}
1176
1177static void
1178store_pool_delete(png_store *ps, store_pool *pool)
1179{
1180 if (pool->list != NULL)
1181 {
1182 fprintf(stderr, "%s: %s %s: memory lost (list follows):\n", ps->test,
1183 pool == &ps->read_memory_pool ? "read" : "write",
1184 pool == &ps->read_memory_pool ? (ps->current != NULL ?
1185 ps->current->name : "unknown file") : ps->wname);
1186 ++ps->nerrors;
1187
1188 do
1189 {
1190 store_memory *next = pool->list;
1191 pool->list = next->next;
1192 next->next = NULL;
1193
Glenn Randers-Pehrson9a75d992010-10-08 16:27:14 -05001194 fprintf(stderr, "\t%lu bytes @ %p\n",
John Bowler9c693602011-02-12 08:58:21 -06001195 (unsigned long)next->size, (PNG_CONST void*)(next+1));
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001196 /* The NULL means this will always return, even if the memory is
1197 * corrupted.
1198 */
1199 store_memory_free(NULL, pool, next);
1200 }
1201 while (pool->list != NULL);
1202 }
1203
1204 /* And reset the other fields too for the next time. */
1205 if (pool->max > pool->max_max) pool->max_max = pool->max;
1206 pool->max = 0;
1207 if (pool->current != 0) /* unexpected internal error */
1208 fprintf(stderr, "%s: %s %s: memory counter mismatch (internal error)\n",
1209 ps->test, pool == &ps->read_memory_pool ? "read" : "write",
1210 pool == &ps->read_memory_pool ? (ps->current != NULL ?
1211 ps->current->name : "unknown file") : ps->wname);
1212 pool->current = 0;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001213
1214 if (pool->limit > pool->max_limit)
1215 pool->max_limit = pool->limit;
1216
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001217 pool->limit = 0;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001218
1219 if (pool->total > pool->max_total)
1220 pool->max_total = pool->total;
1221
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001222 pool->total = 0;
1223
1224 /* Get a new mark too. */
1225 store_pool_mark(pool->mark);
1226}
1227
1228/* The memory callbacks: */
1229static png_voidp
1230store_malloc(png_structp pp, png_alloc_size_t cb)
1231{
1232 store_pool *pool = png_get_mem_ptr(pp);
1233 store_memory *new = malloc(cb + (sizeof *new) + (sizeof pool->mark));
1234
1235 if (new != NULL)
1236 {
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001237 if (cb > pool->max)
1238 pool->max = cb;
1239
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001240 pool->current += cb;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001241
1242 if (pool->current > pool->limit)
1243 pool->limit = pool->current;
1244
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001245 pool->total += cb;
1246
1247 new->size = cb;
1248 memcpy(new->mark, pool->mark, sizeof new->mark);
1249 memcpy((png_byte*)(new+1) + cb, pool->mark, sizeof pool->mark);
1250 new->pool = pool;
1251 new->next = pool->list;
1252 pool->list = new;
1253 ++new;
1254 }
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001255
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001256 else
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001257 store_pool_error(pool->store, pp, "out of memory");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001258
1259 return new;
1260}
1261
1262static void
1263store_free(png_structp pp, png_voidp memory)
1264{
1265 store_pool *pool = png_get_mem_ptr(pp);
1266 store_memory *this = memory, **test;
1267
1268 /* First check that this 'memory' really is valid memory - it must be in the
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001269 * pool list. If it is, use the shared memory_free function to free it.
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001270 */
1271 --this;
1272 for (test = &pool->list; *test != this; test = &(*test)->next)
1273 {
1274 if (*test == NULL)
1275 {
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001276 store_pool_error(pool->store, pp, "bad pointer to free");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001277 return;
1278 }
1279 }
1280
1281 /* Unlink this entry, *test == this. */
1282 *test = this->next;
1283 this->next = NULL;
1284 store_memory_free(pp, pool, this);
1285}
1286
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001287/* Setup functions. */
1288/* Cleanup when aborting a write or after storing the new file. */
1289static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001290store_write_reset(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001291{
1292 if (ps->pwrite != NULL)
1293 {
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001294 anon_context(ps);
1295
1296 Try
1297 png_destroy_write_struct(&ps->pwrite, &ps->piwrite);
1298
1299 Catch_anonymous
1300 {
1301 /* memory corruption: continue. */
1302 }
1303
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001304 ps->pwrite = NULL;
1305 ps->piwrite = NULL;
1306 }
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001307
1308 /* And make sure that all the memory has been freed - this will output
1309 * spurious errors in the case of memory corruption above, but this is safe.
1310 */
1311 store_pool_delete(ps, &ps->write_memory_pool);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001312
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001313 store_freenew(ps);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001314}
1315
1316/* The following is the main write function, it returns a png_struct and,
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001317 * optionally, a png_info suitable for writiing a new PNG file. Use
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001318 * store_storefile above to record this file after it has been written. The
1319 * returned libpng structures as destroyed by store_write_reset above.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001320 */
1321static png_structp
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001322set_store_for_write(png_store *ps, png_infopp ppi,
1323 PNG_CONST char * volatile name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001324{
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001325 anon_context(ps);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001326
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001327 Try
1328 {
1329 if (ps->pwrite != NULL)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001330 png_error(ps->pwrite, "write store already in use");
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001331
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001332 store_write_reset(ps);
1333 safecat(ps->wname, sizeof ps->wname, 0, name);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001334
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001335 /* Don't do the slow memory checks if doing a speed test. */
1336 if (ps->speed)
1337 ps->pwrite = png_create_write_struct(PNG_LIBPNG_VER_STRING,
1338 ps, store_error, store_warning);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001339
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001340 else
1341 ps->pwrite = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
1342 ps, store_error, store_warning, &ps->write_memory_pool,
1343 store_malloc, store_free);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001344
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001345 png_set_write_fn(ps->pwrite, ps, store_write, store_flush);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001346
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001347 if (ppi != NULL)
1348 *ppi = ps->piwrite = png_create_info_struct(ps->pwrite);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001349 }
1350
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001351 Catch_anonymous
1352 return NULL;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001353
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001354 return ps->pwrite;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001355}
1356
Glenn Randers-Pehrsonbc363ec2010-10-12 21:17:00 -05001357/* Cleanup when finished reading (either due to error or in the success case).
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001358 */
1359static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001360store_read_reset(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001361{
1362 if (ps->pread != NULL)
1363 {
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001364 anon_context(ps);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001365
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001366 Try
1367 png_destroy_read_struct(&ps->pread, &ps->piread, NULL);
1368
1369 Catch_anonymous
1370 {
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001371 /* error already output: continue */
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001372 }
1373
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001374 ps->pread = NULL;
1375 ps->piread = NULL;
1376 }
1377
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001378 /* Always do this to be safe. */
1379 store_pool_delete(ps, &ps->read_memory_pool);
1380
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001381 ps->current = NULL;
1382 ps->next = NULL;
1383 ps->readpos = 0;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001384 ps->validated = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001385}
1386
1387static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001388store_read_set(png_store *ps, png_uint_32 id)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001389{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001390 png_store_file *pf = ps->saved;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001391
1392 while (pf != NULL)
1393 {
1394 if (pf->id == id)
1395 {
1396 ps->current = pf;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001397 ps->next = NULL;
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001398 store_read_buffer_next(ps);
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001399 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001400 }
1401
1402 pf = pf->next;
1403 }
1404
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001405 {
1406 size_t pos;
1407 char msg[FILE_NAME_SIZE+64];
1408
1409 pos = standard_name_from_id(msg, sizeof msg, 0, id);
1410 pos = safecat(msg, sizeof msg, pos, ": file not found");
1411 png_error(ps->pread, msg);
1412 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001413}
1414
1415/* The main interface for reading a saved file - pass the id number of the file
1416 * to retrieve. Ids must be unique or the earlier file will be hidden. The API
1417 * returns a png_struct and, optionally, a png_info. Both of these will be
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001418 * destroyed by store_read_reset above.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001419 */
1420static png_structp
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001421set_store_for_read(png_store *ps, png_infopp ppi, png_uint_32 id,
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001422 PNG_CONST char *name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001423{
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001424 /* Set the name for png_error */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001425 safecat(ps->test, sizeof ps->test, 0, name);
1426
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001427 if (ps->pread != NULL)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001428 png_error(ps->pread, "read store already in use");
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001429
1430 store_read_reset(ps);
1431
1432 /* Both the create APIs can return NULL if used in their default mode
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001433 * (because there is no other way of handling an error because the jmp_buf
1434 * by default is stored in png_struct and that has not been allocated!)
1435 * However, given that store_error works correctly in these circumstances
1436 * we don't ever expect NULL in this program.
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001437 */
1438 if (ps->speed)
1439 ps->pread = png_create_read_struct(PNG_LIBPNG_VER_STRING, ps,
1440 store_error, store_warning);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001441
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001442 else
1443 ps->pread = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, ps,
1444 store_error, store_warning, &ps->read_memory_pool, store_malloc,
1445 store_free);
1446
1447 if (ps->pread == NULL)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001448 {
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001449 struct exception_context *the_exception_context = &ps->exception_context;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001450
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001451 store_log(ps, NULL, "png_create_read_struct returned NULL (unexpected)",
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001452 1 /*error*/);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001453
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001454 Throw ps;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001455 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001456
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001457 store_read_set(ps, id);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001458
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001459 if (ppi != NULL)
1460 *ppi = ps->piread = png_create_info_struct(ps->pread);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001461
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001462 return ps->pread;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001463}
1464
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001465/* The overall cleanup of a store simply calls the above then removes all the
1466 * saved files. This does not delete the store itself.
1467 */
1468static void
1469store_delete(png_store *ps)
1470{
1471 store_write_reset(ps);
1472 store_read_reset(ps);
1473 store_freefile(&ps->saved);
John Bowler9994f252011-05-15 18:52:39 -05001474 store_image_free(ps, NULL);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001475}
1476
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001477/*********************** PNG FILE MODIFICATION ON READ ************************/
1478/* Files may be modified on read. The following structure contains a complete
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001479 * png_store together with extra members to handle modification and a special
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001480 * read callback for libpng. To use this the 'modifications' field must be set
1481 * to a list of png_modification structures that actually perform the
1482 * modification, otherwise a png_modifier is functionally equivalent to a
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001483 * png_store. There is a special read function, set_modifier_for_read, which
1484 * replaces set_store_for_read.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001485 */
1486typedef struct png_modifier
1487{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001488 png_store this; /* I am a png_store */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001489 struct png_modification *modifications; /* Changes to make */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001490
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001491 enum modifier_state
1492 {
1493 modifier_start, /* Initial value */
1494 modifier_signature, /* Have a signature */
1495 modifier_IHDR /* Have an IHDR */
1496 } state; /* My state */
1497
1498 /* Information from IHDR: */
1499 png_byte bit_depth; /* From IHDR */
1500 png_byte colour_type; /* From IHDR */
1501
1502 /* While handling PLTE, IDAT and IEND these chunks may be pended to allow
1503 * other chunks to be inserted.
1504 */
1505 png_uint_32 pending_len;
1506 png_uint_32 pending_chunk;
1507
1508 /* Test values */
1509 double *gammas;
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001510 unsigned int ngammas;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001511
1512 /* Lowest sbit to test (libpng fails for sbit < 8) */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001513 png_byte sbitlow;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001514
1515 /* Error control - these are the limits on errors accepted by the gamma tests
1516 * below.
1517 */
1518 double maxout8; /* Maximum output value error */
Glenn Randers-Pehrson21b4b332010-08-18 07:12:38 -05001519 double maxabs8; /* Absolute sample error 0..1 */
John Bowlerd273ad22011-05-07 21:00:28 -05001520 double maxcalc8; /* Absolute sample error 0..1 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001521 double maxpc8; /* Percentage sample error 0..100% */
1522 double maxout16; /* Maximum output value error */
1523 double maxabs16; /* Absolute sample error 0..1 */
John Bowlerd273ad22011-05-07 21:00:28 -05001524 double maxcalc16;/* Absolute sample error 0..1 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001525 double maxpc16; /* Percentage sample error 0..100% */
1526
John Bowler6f55ee22011-06-11 07:28:06 -05001527 /* Log limits - values above this are logged, but not necessarily
1528 * warned.
1529 */
1530 double log8; /* Absolute error in 8 bits to log */
1531 double log16; /* Absolute error in 16 bits to log */
1532
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001533 /* Logged 8 and 16 bit errors ('output' values): */
1534 double error_gray_2;
1535 double error_gray_4;
1536 double error_gray_8;
1537 double error_gray_16;
1538 double error_color_8;
1539 double error_color_16;
John Bowler1921e6d2011-05-16 20:57:54 -05001540 double error_indexed;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001541
1542 /* Flags: */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001543 /* Whether or not to interlace. */
1544 int interlace_type :9; /* int, but must store '1' */
1545
John Bowlerb54498e2010-12-08 16:26:21 -06001546 /* Run the standard tests? */
1547 unsigned int test_standard :1;
1548
John Bowler660c6e42010-12-19 06:22:23 -06001549 /* Run the odd-sized image and interlace read/write tests? */
1550 unsigned int test_size :1;
1551
John Bowlerf21a0d02011-01-23 23:55:19 -06001552 /* Run tests on reading with a combiniation of transforms, */
1553 unsigned int test_transform :1;
1554
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001555 /* When to use the use_input_precision option: */
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001556 unsigned int use_input_precision :1;
1557 unsigned int use_input_precision_sbit :1;
1558 unsigned int use_input_precision_16to8 :1;
John Bowlerb54498e2010-12-08 16:26:21 -06001559
John Bowler5441e182011-05-18 18:57:12 -05001560 /* If set assume that the calculation bit depth is set by the input
1561 * precision, not the output precision.
1562 */
1563 unsigned int calculations_use_input_precision :1;
1564
1565 /* If set assume that the calculations are done in 16 bits even if both input
1566 * and output are 8 bit or less.
1567 */
1568 unsigned int assume_16_bit_calculations :1;
John Bowlerd273ad22011-05-07 21:00:28 -05001569
John Bowlerb54498e2010-12-08 16:26:21 -06001570 /* Which gamma tests to run: */
John Bowlerf21a0d02011-01-23 23:55:19 -06001571 unsigned int test_gamma_threshold :1;
1572 unsigned int test_gamma_transform :1; /* main tests */
1573 unsigned int test_gamma_sbit :1;
1574 unsigned int test_gamma_strip16 :1;
John Bowlerd273ad22011-05-07 21:00:28 -05001575 unsigned int test_gamma_background :1;
1576 unsigned int test_gamma_alpha_mode :1;
1577 unsigned int test_gamma_expand16 :1;
John Bowlerb54498e2010-12-08 16:26:21 -06001578
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001579 unsigned int log :1; /* Log max error */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001580
1581 /* Buffer information, the buffer size limits the size of the chunks that can
1582 * be modified - they must fit (including header and CRC) into the buffer!
1583 */
1584 size_t flush; /* Count of bytes to flush */
1585 size_t buffer_count; /* Bytes in buffer */
1586 size_t buffer_position; /* Position in buffer */
1587 png_byte buffer[1024];
1588} png_modifier;
1589
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001590/* This returns true if the test should be stopped now because it has already
1591 * failed and it is running silently.
1592 */
1593static int fail(png_modifier *pm)
1594{
1595 return !pm->log && !pm->this.verbose && (pm->this.nerrors > 0 ||
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001596 (pm->this.treat_warnings_as_errors && pm->this.nwarnings > 0));
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001597}
1598
1599static void
1600modifier_init(png_modifier *pm)
1601{
1602 memset(pm, 0, sizeof *pm);
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001603 store_init(&pm->this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001604 pm->modifications = NULL;
1605 pm->state = modifier_start;
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001606 pm->sbitlow = 1U;
John Bowlerd273ad22011-05-07 21:00:28 -05001607 pm->ngammas = 0;
1608 pm->gammas = 0;
1609 pm->maxout8 = pm->maxpc8 = pm->maxabs8 = pm->maxcalc8 = 0;
1610 pm->maxout16 = pm->maxpc16 = pm->maxabs16 = pm->maxcalc16 = 0;
John Bowler6f55ee22011-06-11 07:28:06 -05001611 pm->log8 = pm->log16 = 0; /* Means 'off' */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001612 pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0;
1613 pm->error_gray_16 = pm->error_color_8 = pm->error_color_16 = 0;
John Bowler1921e6d2011-05-16 20:57:54 -05001614 pm->error_indexed = 0;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001615 pm->interlace_type = PNG_INTERLACE_NONE;
John Bowlerafea7d12011-01-28 06:38:14 -06001616 pm->test_standard = 0;
John Bowler660c6e42010-12-19 06:22:23 -06001617 pm->test_size = 0;
John Bowlerafea7d12011-01-28 06:38:14 -06001618 pm->test_transform = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001619 pm->use_input_precision = 0;
1620 pm->use_input_precision_sbit = 0;
1621 pm->use_input_precision_16to8 = 0;
John Bowler5441e182011-05-18 18:57:12 -05001622 pm->calculations_use_input_precision = 0;
John Bowlerafea7d12011-01-28 06:38:14 -06001623 pm->test_gamma_threshold = 0;
1624 pm->test_gamma_transform = 0;
1625 pm->test_gamma_sbit = 0;
1626 pm->test_gamma_strip16 = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05001627 pm->test_gamma_background = 0;
1628 pm->test_gamma_alpha_mode = 0;
1629 pm->test_gamma_expand16 = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001630 pm->log = 0;
1631
1632 /* Rely on the memset for all the other fields - there are no pointers */
1633}
1634
John Bowler4a12f4a2011-04-17 18:34:22 -05001635#ifdef PNG_READ_TRANSFORMS_SUPPORTED
John Bowler5441e182011-05-18 18:57:12 -05001636/* If pm->calculations_use_input_precision is set then operations will happen
1637 * with only 8 bit precision unless both the input and output bit depth are 16.
1638 *
1639 * If pm->assume_16_bit_calculations is set then even 8 bit calculations use 16
1640 * bit precision. This only affects those of the following limits that pertain
1641 * to a calculation - not a digitization operation!
1642 */
1643static double abserr(png_modifier *pm, int in_depth, int out_depth)
John Bowler4a12f4a2011-04-17 18:34:22 -05001644{
John Bowler5441e182011-05-18 18:57:12 -05001645 /* Absolute error permitted in linear values - affected by the bit depth of
1646 * the calculations.
John Bowlerd273ad22011-05-07 21:00:28 -05001647 */
John Bowler5441e182011-05-18 18:57:12 -05001648 if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 ||
1649 !pm->calculations_use_input_precision)))
1650 return pm->maxabs16;
John Bowlerd273ad22011-05-07 21:00:28 -05001651 else
John Bowler5441e182011-05-18 18:57:12 -05001652 return pm->maxabs8;
John Bowlerd273ad22011-05-07 21:00:28 -05001653}
1654
John Bowler5441e182011-05-18 18:57:12 -05001655static double calcerr(png_modifier *pm, int in_depth, int out_depth)
John Bowler4a12f4a2011-04-17 18:34:22 -05001656{
John Bowler5441e182011-05-18 18:57:12 -05001657 /* Error in the linear composition arithmetic - only relevant when
1658 * composition actually happens (0 < alpha < 1).
1659 */
1660 if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 ||
1661 !pm->calculations_use_input_precision)))
1662 return pm->maxcalc16;
1663 else
1664 return pm->maxcalc8;
John Bowler4a12f4a2011-04-17 18:34:22 -05001665}
1666
John Bowler5441e182011-05-18 18:57:12 -05001667static double pcerr(png_modifier *pm, int in_depth, int out_depth)
1668{
1669 /* Percentage error permitted in the linear values. Note that the specified
1670 * value is a percentage but this routine returns a simple number.
1671 */
1672 if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 ||
1673 !pm->calculations_use_input_precision)))
1674 return pm->maxpc16 * .01;
1675 else
1676 return pm->maxpc8 * .01;
1677}
1678
1679/* Output error - the error in the encoded value. This is determined by the
1680 * digitization of the output so can be +/-0.5 in the actual output value. In
1681 * the expand_16 case with the current code in libpng the expand happens after
1682 * all the calculations are done in 8 bit arithmetic, so even though the output
1683 * depth is 16 the output error is determined by the 8 bit calculation.
1684 *
1685 * This limit is not determined by the bit depth of internal calculations.
1686 *
1687 * The specified parameter does *not* include the base .5 digitization error but
1688 * it is added here.
1689 */
1690static double outerr(png_modifier *pm, int in_depth, int out_depth)
John Bowler4a12f4a2011-04-17 18:34:22 -05001691{
1692 /* There is a serious error in the 2 and 4 bit grayscale transform because
1693 * the gamma table value (8 bits) is simply shifted, not rounded, so the
1694 * error in 4 bit greyscale gamma is up to the value below. This is a hack
1695 * to allow pngvalid to succeed:
John Bowler5441e182011-05-18 18:57:12 -05001696 *
1697 * TODO: fix this in libpng
John Bowler4a12f4a2011-04-17 18:34:22 -05001698 */
John Bowler5441e182011-05-18 18:57:12 -05001699 if (out_depth == 2)
John Bowler4a12f4a2011-04-17 18:34:22 -05001700 return .73182-.5;
1701
John Bowler5441e182011-05-18 18:57:12 -05001702 if (out_depth == 4)
John Bowler4a12f4a2011-04-17 18:34:22 -05001703 return .90644-.5;
1704
John Bowler5441e182011-05-18 18:57:12 -05001705 if (out_depth == 16 && (in_depth == 16 ||
1706 !pm->calculations_use_input_precision))
1707 return pm->maxout16;
John Bowler4a12f4a2011-04-17 18:34:22 -05001708
John Bowler5441e182011-05-18 18:57:12 -05001709 /* This is the case where the value was calculated at 8-bit precision then
1710 * scaled to 16 bits.
1711 */
1712 else if (out_depth == 16)
1713 return pm->maxout8 * 257;
1714
1715 else
1716 return pm->maxout8;
1717}
1718
John Bowler6f55ee22011-06-11 07:28:06 -05001719/* This does the same thing as the above however it returns the value to log,
1720 * rather than raising a warning. This is useful for debugging to track down
1721 * exactly what set of parameters cause high error values.
1722 */
1723static double outlog(png_modifier *pm, int in_depth, int out_depth)
1724{
1725 /* The command line parameters are either 8 bit (0..255) or 16 bit (0..65535)
1726 * and so must be adjusted for low bit depth grayscale:
1727 */
1728 if (out_depth <= 8)
1729 {
1730 if (pm->log8 == 0) /* switched off */
1731 return 256;
1732
1733 if (out_depth < 8)
1734 return pm->log8 / 255 * ((1<<out_depth)-1);
1735
1736 return pm->log8;
1737 }
1738
1739 if (out_depth == 16 && (in_depth == 16 ||
1740 !pm->calculations_use_input_precision))
1741 {
1742 if (pm->log16 == 0)
1743 return 65536;
1744
1745 return pm->log16;
1746 }
1747
1748 /* This is the case where the value was calculated at 8-bit precision then
1749 * scaled to 16 bits.
1750 */
1751 if (pm->log8 == 0)
1752 return 65536;
1753
1754 return pm->log8 * 257;
1755}
1756
John Bowler5441e182011-05-18 18:57:12 -05001757/* This complements the above by providing the appropriate quantization for the
1758 * final value. Normally this would just be quantization to an integral value,
1759 * but in the 8 bit calculation case it's actually quantization to a multiple of
1760 * 257!
1761 */
1762static int output_quantization_factor(png_modifier *pm, int in_depth,
1763 int out_depth)
1764{
1765 if (out_depth == 16 && in_depth != 16
1766 && pm->calculations_use_input_precision)
1767 return 257;
1768 else
1769 return 1;
John Bowler4a12f4a2011-04-17 18:34:22 -05001770}
1771
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05001772/* One modification structure must be provided for each chunk to be modified (in
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001773 * fact more than one can be provided if multiple separate changes are desired
1774 * for a single chunk.) Modifications include adding a new chunk when a
1775 * suitable chunk does not exist.
1776 *
1777 * The caller of modify_fn will reset the CRC of the chunk and record 'modified'
1778 * or 'added' as appropriate if the modify_fn returns 1 (true). If the
1779 * modify_fn is NULL the chunk is simply removed.
1780 */
1781typedef struct png_modification
1782{
1783 struct png_modification *next;
1784 png_uint_32 chunk;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001785
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001786 /* If the following is NULL all matching chunks will be removed: */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001787 int (*modify_fn)(struct png_modifier *pm,
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001788 struct png_modification *me, int add);
1789
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001790 /* If the following is set to PLTE, IDAT or IEND and the chunk has not been
1791 * found and modified (and there is a modify_fn) the modify_fn will be called
1792 * to add the chunk before the relevant chunk.
1793 */
1794 png_uint_32 add;
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05001795 unsigned int modified :1; /* Chunk was modified */
1796 unsigned int added :1; /* Chunk was added */
1797 unsigned int removed :1; /* Chunk was removed */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001798} png_modification;
1799
1800static void modification_reset(png_modification *pmm)
1801{
1802 if (pmm != NULL)
1803 {
1804 pmm->modified = 0;
1805 pmm->added = 0;
1806 pmm->removed = 0;
1807 modification_reset(pmm->next);
1808 }
1809}
1810
1811static void
1812modification_init(png_modification *pmm)
1813{
1814 memset(pmm, 0, sizeof *pmm);
1815 pmm->next = NULL;
1816 pmm->chunk = 0;
1817 pmm->modify_fn = NULL;
1818 pmm->add = 0;
1819 modification_reset(pmm);
1820}
1821
1822static void
1823modifier_reset(png_modifier *pm)
1824{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001825 store_read_reset(&pm->this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001826 pm->modifications = NULL;
1827 pm->state = modifier_start;
1828 pm->bit_depth = pm->colour_type = 0;
1829 pm->pending_len = pm->pending_chunk = 0;
1830 pm->flush = pm->buffer_count = pm->buffer_position = 0;
1831}
1832
1833/* Convenience macros. */
1834#define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
1835#define CHUNK_IHDR CHUNK(73,72,68,82)
1836#define CHUNK_PLTE CHUNK(80,76,84,69)
1837#define CHUNK_IDAT CHUNK(73,68,65,84)
1838#define CHUNK_IEND CHUNK(73,69,78,68)
1839#define CHUNK_cHRM CHUNK(99,72,82,77)
1840#define CHUNK_gAMA CHUNK(103,65,77,65)
1841#define CHUNK_sBIT CHUNK(115,66,73,84)
1842#define CHUNK_sRGB CHUNK(115,82,71,66)
1843
1844/* The guts of modification are performed during a read. */
1845static void
1846modifier_crc(png_bytep buffer)
1847{
1848 /* Recalculate the chunk CRC - a complete chunk must be in
1849 * the buffer, at the start.
1850 */
1851 uInt datalen = png_get_uint_32(buffer);
1852 png_save_uint_32(buffer+datalen+8, crc32(0L, buffer+4, datalen+4));
1853}
1854
1855static void
1856modifier_setbuffer(png_modifier *pm)
1857{
1858 modifier_crc(pm->buffer);
1859 pm->buffer_count = png_get_uint_32(pm->buffer)+12;
1860 pm->buffer_position = 0;
1861}
1862
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001863/* Separate the callback into the actual implementation (which is passed the
1864 * png_modifier explicitly) and the callback, which gets the modifier from the
1865 * png_struct.
1866 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001867static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001868modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001869{
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001870 while (st > 0)
1871 {
1872 size_t cb;
1873 png_uint_32 len, chunk;
1874 png_modification *mod;
1875
1876 if (pm->buffer_position >= pm->buffer_count) switch (pm->state)
1877 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001878 static png_byte sign[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
1879 case modifier_start:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001880 store_read_imp(&pm->this, pm->buffer, 8); /* size of signature. */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001881 pm->buffer_count = 8;
1882 pm->buffer_position = 0;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001883
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001884 if (memcmp(pm->buffer, sign, 8) != 0)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001885 png_error(pm->this.pread, "invalid PNG file signature");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001886 pm->state = modifier_signature;
1887 break;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001888
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001889 case modifier_signature:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001890 store_read_imp(&pm->this, pm->buffer, 13+12); /* size of IHDR */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001891 pm->buffer_count = 13+12;
1892 pm->buffer_position = 0;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001893
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001894 if (png_get_uint_32(pm->buffer) != 13 ||
1895 png_get_uint_32(pm->buffer+4) != CHUNK_IHDR)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001896 png_error(pm->this.pread, "invalid IHDR");
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001897
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001898 /* Check the list of modifiers for modifications to the IHDR. */
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001899 mod = pm->modifications;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001900 while (mod != NULL)
1901 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001902 if (mod->chunk == CHUNK_IHDR && mod->modify_fn &&
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001903 (*mod->modify_fn)(pm, mod, 0))
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001904 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001905 mod->modified = 1;
1906 modifier_setbuffer(pm);
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001907 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001908
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001909 /* Ignore removal or add if IHDR! */
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001910 mod = mod->next;
1911 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001912
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001913 /* Cache information from the IHDR (the modified one.) */
1914 pm->bit_depth = pm->buffer[8+8];
1915 pm->colour_type = pm->buffer[8+8+1];
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001916
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001917 pm->state = modifier_IHDR;
1918 pm->flush = 0;
1919 break;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001920
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001921 case modifier_IHDR:
1922 default:
1923 /* Read a new chunk and process it until we see PLTE, IDAT or
1924 * IEND. 'flush' indicates that there is still some data to
1925 * output from the preceding chunk.
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001926 */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001927 if ((cb = pm->flush) > 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001928 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001929 if (cb > st) cb = st;
1930 pm->flush -= cb;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001931 store_read_imp(&pm->this, pb, cb);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001932 pb += cb;
1933 st -= cb;
John Bowlerd2f0bc22011-06-11 06:42:06 -05001934 if (st == 0) return;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001935 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001936
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001937 /* No more bytes to flush, read a header, or handle a pending
1938 * chunk.
1939 */
1940 if (pm->pending_chunk != 0)
1941 {
1942 png_save_uint_32(pm->buffer, pm->pending_len);
1943 png_save_uint_32(pm->buffer+4, pm->pending_chunk);
1944 pm->pending_len = 0;
1945 pm->pending_chunk = 0;
1946 }
1947 else
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001948 store_read_imp(&pm->this, pm->buffer, 8);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001949
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001950 pm->buffer_count = 8;
1951 pm->buffer_position = 0;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001952
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001953 /* Check for something to modify or a terminator chunk. */
1954 len = png_get_uint_32(pm->buffer);
1955 chunk = png_get_uint_32(pm->buffer+4);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001956
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001957 /* Terminators first, they may have to be delayed for added
1958 * chunks
1959 */
1960 if (chunk == CHUNK_PLTE || chunk == CHUNK_IDAT ||
1961 chunk == CHUNK_IEND)
1962 {
1963 mod = pm->modifications;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001964
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001965 while (mod != NULL)
1966 {
1967 if ((mod->add == chunk ||
1968 (mod->add == CHUNK_PLTE && chunk == CHUNK_IDAT)) &&
1969 mod->modify_fn != NULL && !mod->modified && !mod->added)
1970 {
1971 /* Regardless of what the modify function does do not run
1972 * this again.
1973 */
1974 mod->added = 1;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001975
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001976 if ((*mod->modify_fn)(pm, mod, 1 /*add*/))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001977 {
1978 /* Reset the CRC on a new chunk */
1979 if (pm->buffer_count > 0)
1980 modifier_setbuffer(pm);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001981
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001982 else
1983 {
1984 pm->buffer_position = 0;
1985 mod->removed = 1;
1986 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001987
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001988 /* The buffer has been filled with something (we assume)
1989 * so output this. Pend the current chunk.
1990 */
1991 pm->pending_len = len;
1992 pm->pending_chunk = chunk;
1993 break; /* out of while */
1994 }
1995 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001996
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001997 mod = mod->next;
1998 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001999
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002000 /* Don't do any further processing if the buffer was modified -
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06002001 * otherwise the code will end up modifying a chunk that was
2002 * just added.
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002003 */
2004 if (mod != NULL)
2005 break; /* out of switch */
2006 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06002007
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06002008 /* If we get to here then this chunk may need to be modified. To
2009 * do this it must be less than 1024 bytes in total size, otherwise
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002010 * it just gets flushed.
2011 */
2012 if (len+12 <= sizeof pm->buffer)
2013 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002014 store_read_imp(&pm->this, pm->buffer+pm->buffer_count,
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002015 len+12-pm->buffer_count);
2016 pm->buffer_count = len+12;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06002017
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002018 /* Check for a modification, else leave it be. */
2019 mod = pm->modifications;
2020 while (mod != NULL)
2021 {
2022 if (mod->chunk == chunk)
2023 {
2024 if (mod->modify_fn == NULL)
2025 {
2026 /* Remove this chunk */
2027 pm->buffer_count = pm->buffer_position = 0;
2028 mod->removed = 1;
2029 break; /* Terminate the while loop */
2030 }
2031
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002032 else if ((*mod->modify_fn)(pm, mod, 0))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002033 {
2034 mod->modified = 1;
2035 /* The chunk may have been removed: */
2036 if (pm->buffer_count == 0)
2037 {
2038 pm->buffer_position = 0;
2039 break;
2040 }
2041 modifier_setbuffer(pm);
2042 }
2043 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06002044
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002045 mod = mod->next;
2046 }
2047 }
2048
2049 else
2050 pm->flush = len+12 - pm->buffer_count; /* data + crc */
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06002051
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002052 /* Take the data from the buffer (if there is any). */
2053 break;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002054 }
2055
2056 /* Here to read from the modifier buffer (not directly from
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05002057 * the store, as in the flush case above.)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002058 */
2059 cb = pm->buffer_count - pm->buffer_position;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002060
2061 if (cb > st)
2062 cb = st;
2063
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002064 memcpy(pb, pm->buffer + pm->buffer_position, cb);
2065 st -= cb;
2066 pb += cb;
2067 pm->buffer_position += cb;
2068 }
2069}
2070
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002071/* The callback: */
2072static void
2073modifier_read(png_structp pp, png_bytep pb, png_size_t st)
2074{
2075 png_modifier *pm = png_get_io_ptr(pp);
2076
2077 if (pm == NULL || pm->this.pread != pp)
2078 png_error(pp, "bad modifier_read call");
2079
2080 modifier_read_imp(pm, pb, st);
2081}
2082
2083/* Like store_progressive_read but the data is getting changed as we go so we
2084 * need a local buffer.
2085 */
2086static void
2087modifier_progressive_read(png_modifier *pm, png_structp pp, png_infop pi)
2088{
2089 if (pm->this.pread != pp || pm->this.current == NULL ||
2090 pm->this.next == NULL)
2091 png_error(pp, "store state damaged (progressive)");
2092
2093 /* This is another Horowitz and Hill random noise generator. In this case
2094 * the aim is to stress the progressive reader with truely horrible variable
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06002095 * buffer sizes in the range 1..500, so a sequence of 9 bit random numbers
2096 * is generated. We could probably just count from 1 to 32767 and get as
2097 * good a result.
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002098 */
2099 for (;;)
2100 {
2101 static png_uint_32 noise = 1;
2102 png_size_t cb, cbAvail;
2103 png_byte buffer[512];
2104
2105 /* Generate 15 more bits of stuff: */
2106 noise = (noise << 9) | ((noise ^ (noise >> (9-5))) & 0x1ff);
2107 cb = noise & 0x1ff;
2108
2109 /* Check that this number of bytes are available (in the current buffer.)
2110 * (This doesn't quite work - the modifier might delete a chunk; unlikely
2111 * but possible, it doesn't happen at present because the modifier only
2112 * adds chunks to standard images.)
2113 */
2114 cbAvail = store_read_buffer_avail(&pm->this);
2115 if (pm->buffer_count > pm->buffer_position)
2116 cbAvail += pm->buffer_count - pm->buffer_position;
2117
2118 if (cb > cbAvail)
2119 {
2120 /* Check for EOF: */
2121 if (cbAvail == 0)
2122 break;
2123
2124 cb = cbAvail;
2125 }
2126
2127 modifier_read_imp(pm, buffer, cb);
2128 png_process_data(pp, pi, buffer, cb);
2129 }
2130
2131 /* Check the invariants at the end (if this fails it's a problem in this
2132 * file!)
2133 */
2134 if (pm->buffer_count > pm->buffer_position ||
2135 pm->this.next != &pm->this.current->data ||
2136 pm->this.readpos < pm->this.current->datacount)
2137 png_error(pp, "progressive read implementation error");
2138}
2139
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002140/* Set up a modifier. */
2141static png_structp
2142set_modifier_for_read(png_modifier *pm, png_infopp ppi, png_uint_32 id,
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05002143 PNG_CONST char *name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002144{
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05002145 /* Do this first so that the modifier fields are cleared even if an error
2146 * happens allocating the png_struct. No allocation is done here so no
2147 * cleanup is required.
2148 */
2149 pm->state = modifier_start;
2150 pm->bit_depth = 0;
2151 pm->colour_type = 255;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002152
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05002153 pm->pending_len = 0;
2154 pm->pending_chunk = 0;
2155 pm->flush = 0;
2156 pm->buffer_count = 0;
2157 pm->buffer_position = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002158
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05002159 return set_store_for_read(&pm->this, ppi, id, name);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002160}
John Bowler4a12f4a2011-04-17 18:34:22 -05002161#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002162
2163/***************************** STANDARD PNG FILES *****************************/
2164/* Standard files - write and save standard files. */
John Bowler660c6e42010-12-19 06:22:23 -06002165/* There are two basic forms of standard images. Those which attempt to have
2166 * all the possible pixel values (not possible for 16bpp images, but a range of
2167 * values are produced) and those which have a range of image sizes. The former
2168 * are used for testing transforms, in particular gamma correction and bit
2169 * reduction and increase. The latter are reserved for testing the behavior of
2170 * libpng with respect to 'odd' image sizes - particularly small images where
2171 * rows become 1 byte and interlace passes disappear.
2172 *
2173 * The first, most useful, set are the 'transform' images, the second set of
2174 * small images are the 'size' images.
2175 *
2176 * The transform files are constructed with rows which fit into a 1024 byte row
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002177 * buffer. This makes allocation easier below. Further regardless of the file
John Bowler660c6e42010-12-19 06:22:23 -06002178 * format every row has 128 pixels (giving 1024 bytes for 64bpp formats).
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002179 *
2180 * Files are stored with no gAMA or sBIT chunks, with a PLTE only when needed
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002181 * and with an ID derived from the colour type, bit depth and interlace type
John Bowler660c6e42010-12-19 06:22:23 -06002182 * as above (FILEID). The width (128) and height (variable) are not stored in
2183 * the FILEID - instead the fields are set to 0, indicating a transform file.
2184 *
2185 * The size files ar constructed with rows a maximum of 128 bytes wide, allowing
2186 * a maximum width of 16 pixels (for the 64bpp case.) They also have a maximum
2187 * height of 16 rows. The width and height are stored in the FILEID and, being
2188 * non-zero, indicate a size file.
John Bowler9994f252011-05-15 18:52:39 -05002189 *
2190 * For palette image (colour type 3) multiple transform images are stored with
2191 * the same bit depth to allow testing of more colour combinations -
2192 * particularly important for testing the gamma code because libpng uses a
2193 * different code path for palette images. For size images a single palette is
2194 * used.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002195 */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002196
John Bowler9994f252011-05-15 18:52:39 -05002197/* Make a 'standard' palette. Because there are only 256 entries in a palette
2198 * (maximum) this actually makes a random palette in the hope that enough tests
2199 * will catch enough errors. (Note that the same palette isn't produced every
2200 * time for the same test - it depends on what previous tests have been run -
2201 * but a given set of arguments to pngvalid will always produce the same palette
2202 * at the same test! This is why pseudo-random number generators are useful for
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -05002203 * testing.)
John Bowler9994f252011-05-15 18:52:39 -05002204 *
2205 * The store must be open for write when this is called, otherwise an internal
2206 * error will occur. This routine contains its own magic number seed, so the
2207 * palettes generated don't change if there are intervening errors (changing the
2208 * calls to the store_mark seed.)
2209 */
2210static store_palette_entry *
2211make_standard_palette(png_store* ps, int npalette, int do_tRNS)
2212{
2213 static png_uint_32 palette_seed[2] = { 0x87654321, 9 };
2214
2215 int i = 0;
2216 png_byte values[256][4];
2217
2218 /* Always put in black and white plus the six primary and secondary colors.
2219 */
2220 for (; i<8; ++i)
2221 {
2222 values[i][1] = (i&1) ? 255 : 0;
2223 values[i][2] = (i&2) ? 255 : 0;
2224 values[i][3] = (i&4) ? 255 : 0;
2225 }
2226
2227 /* Then add 62 greys (one quarter of the remaining 256 slots). */
2228 {
2229 int j = 0;
2230 png_byte random_bytes[4];
2231 png_byte need[256];
2232
2233 need[0] = 0; /*got black*/
2234 memset(need+1, 1, (sizeof need)-2); /*need these*/
2235 need[255] = 0; /*but not white*/
2236
2237 while (i<70)
2238 {
2239 png_byte b;
2240
2241 if (j==0)
2242 {
2243 make_four_random_bytes(palette_seed, random_bytes);
2244 j = 4;
2245 }
2246
2247 b = random_bytes[--j];
2248 if (need[b])
2249 {
2250 values[i][1] = b;
2251 values[i][2] = b;
2252 values[i++][3] = b;
2253 }
2254 }
2255 }
2256
2257 /* Finally add 192 colors at random - don't worry about matches to things we
2258 * already have, chance is less than 1/65536. Don't worry about greys,
2259 * chance is the same, so we get a duplicate or extra gray less than 1 time
2260 * in 170.
2261 */
2262 for (; i<256; ++i)
2263 make_four_random_bytes(palette_seed, values[i]);
2264
2265 /* Fill in the alpha values in the first byte. Just use all possible values
2266 * (0..255) in an apparently random order:
2267 */
2268 {
2269 store_palette_entry *palette;
2270 png_byte selector[4];
2271
2272 make_four_random_bytes(palette_seed, selector);
2273
2274 if (do_tRNS)
2275 for (i=0; i<256; ++i)
2276 values[i][0] = (png_byte)(i ^ selector[0]);
2277
2278 else
2279 for (i=0; i<256; ++i)
2280 values[i][0] = 255; /* no transparency/tRNS chunk */
2281
2282 /* 'values' contains 256 ARGB values, but we only need 'npalette'.
2283 * 'npalette' will always be a power of 2: 2, 4, 16 or 256. In the low
2284 * bit depth cases select colors at random, else it is difficult to have
2285 * a set of low bit depth palette test with any chance of a reasonable
2286 * range of colors. Do this by randomly permuting values into the low
2287 * 'npalette' entries using an XOR mask generated here. This also
2288 * permutes the npalette == 256 case in a potentially useful way (there is
2289 * no relationship between palette index and the color value therein!)
2290 */
2291 palette = store_write_palette(ps, npalette);
2292
2293 for (i=0; i<npalette; ++i)
2294 {
2295 palette[i].alpha = values[i ^ selector[1]][0];
2296 palette[i].red = values[i ^ selector[1]][1];
2297 palette[i].green = values[i ^ selector[1]][2];
2298 palette[i].blue = values[i ^ selector[1]][3];
2299 }
2300
2301 return palette;
2302 }
2303}
2304
2305/* Initialize a standard palette on a write stream. The 'do_tRNS' argument
2306 * indicates whether or not to also set the tRNS chunk.
2307 */
2308static void
2309init_standard_palette(png_store *ps, png_structp pp, png_infop pi, int npalette,
2310 int do_tRNS)
2311{
2312 store_palette_entry *ppal = make_standard_palette(ps, npalette, do_tRNS);
2313
2314 {
2315 int i;
2316 png_color palette[256];
2317
2318 /* Set all entries to detect overread errors. */
2319 for (i=0; i<npalette; ++i)
2320 {
2321 palette[i].red = ppal[i].red;
2322 palette[i].green = ppal[i].green;
2323 palette[i].blue = ppal[i].blue;
2324 }
2325
2326 /* Just in case fill in the rest with detectable values: */
2327 for (; i<256; ++i)
2328 palette[i].red = palette[i].green = palette[i].blue = 42;
2329
2330 png_set_PLTE(pp, pi, palette, npalette);
2331 }
2332
2333 if (do_tRNS)
2334 {
2335 int i, j;
2336 png_byte tRNS[256];
2337
2338 /* Set all the entries, but skip trailing opaque entries */
2339 for (i=j=0; i<npalette; ++i)
2340 if ((tRNS[i] = ppal[i].alpha) < 255)
2341 j = i+1;
2342
2343 /* Fill in the remainder with a detectable value: */
2344 for (; i<256; ++i)
2345 tRNS[i] = 24;
2346
2347 if (j > 0)
2348 png_set_tRNS(pp, pi, tRNS, j, 0/*color*/);
2349 }
2350}
2351
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -05002352/* The number of passes is related to the interlace type. There was no libpng
John Bowler660c6e42010-12-19 06:22:23 -06002353 * API to determine this prior to 1.5, so we need an inquiry function:
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002354 */
2355static int
2356npasses_from_interlace_type(png_structp pp, int interlace_type)
2357{
2358 switch (interlace_type)
2359 {
2360 default:
2361 png_error(pp, "invalid interlace type");
2362
2363 case PNG_INTERLACE_NONE:
2364 return 1;
2365
2366 case PNG_INTERLACE_ADAM7:
John Bowler660c6e42010-12-19 06:22:23 -06002367 return PNG_INTERLACE_ADAM7_PASSES;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002368 }
2369}
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002370
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05002371static unsigned int
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002372bit_size(png_structp pp, png_byte colour_type, png_byte bit_depth)
2373{
2374 switch (colour_type)
2375 {
John Bowlerd2f0bc22011-06-11 06:42:06 -05002376 default: png_error(pp, "invalid color type");
2377
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002378 case 0: return bit_depth;
2379
2380 case 2: return 3*bit_depth;
2381
2382 case 3: return bit_depth;
2383
2384 case 4: return 2*bit_depth;
2385
2386 case 6: return 4*bit_depth;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002387 }
2388}
2389
John Bowler660c6e42010-12-19 06:22:23 -06002390#define TRANSFORM_WIDTH 128U
2391#define TRANSFORM_ROWMAX (TRANSFORM_WIDTH*8U)
2392#define SIZE_ROWMAX (16*8U) /* 16 pixels, max 8 bytes each - 128 bytes */
2393#define STANDARD_ROWMAX TRANSFORM_ROWMAX /* The larger of the two */
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05002394#define SIZE_HEIGHTMAX 16 /* Maximum range of size images */
John Bowler660c6e42010-12-19 06:22:23 -06002395
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002396static size_t
John Bowler660c6e42010-12-19 06:22:23 -06002397transform_rowsize(png_structp pp, png_byte colour_type, png_byte bit_depth)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002398{
John Bowler660c6e42010-12-19 06:22:23 -06002399 return (TRANSFORM_WIDTH * bit_size(pp, colour_type, bit_depth)) / 8;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002400}
2401
John Bowler660c6e42010-12-19 06:22:23 -06002402/* transform_width(pp, colour_type, bit_depth) current returns the same number
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05002403 * every time, so just use a macro:
2404 */
John Bowler660c6e42010-12-19 06:22:23 -06002405#define transform_width(pp, colour_type, bit_depth) TRANSFORM_WIDTH
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002406
2407static png_uint_32
John Bowler660c6e42010-12-19 06:22:23 -06002408transform_height(png_structp pp, png_byte colour_type, png_byte bit_depth)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002409{
2410 switch (bit_size(pp, colour_type, bit_depth))
2411 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002412 case 1:
2413 case 2:
2414 case 4:
2415 return 1; /* Total of 128 pixels */
2416
2417 case 8:
2418 return 2; /* Total of 256 pixels/bytes */
2419
2420 case 16:
2421 return 512; /* Total of 65536 pixels */
2422
2423 case 24:
2424 case 32:
2425 return 512; /* 65536 pixels */
2426
2427 case 48:
2428 case 64:
2429 return 2048;/* 4 x 65536 pixels. */
John Bowler9994f252011-05-15 18:52:39 -05002430# define TRANSFORM_HEIGHTMAX 2048
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002431
2432 default:
2433 return 0; /* Error, will be caught later */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002434 }
2435}
2436
John Bowler660c6e42010-12-19 06:22:23 -06002437/* The following can only be defined here, now we have the definitions
2438 * of the transform image sizes.
2439 */
2440static png_uint_32
2441standard_width(png_structp pp, png_uint_32 id)
2442{
2443 png_uint_32 width = WIDTH_FROM_ID(id);
John Bowlerafea7d12011-01-28 06:38:14 -06002444 UNUSED(pp)
John Bowler660c6e42010-12-19 06:22:23 -06002445
2446 if (width == 0)
2447 width = transform_width(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
2448
2449 return width;
2450}
2451
2452static png_uint_32
2453standard_height(png_structp pp, png_uint_32 id)
2454{
2455 png_uint_32 height = HEIGHT_FROM_ID(id);
2456
2457 if (height == 0)
2458 height = transform_height(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
2459
2460 return height;
2461}
2462
2463static png_uint_32
2464standard_rowsize(png_structp pp, png_uint_32 id)
2465{
2466 png_uint_32 width = standard_width(pp, id);
2467
2468 /* This won't overflow: */
2469 width *= bit_size(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
2470 return (width + 7) / 8;
2471}
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002472
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002473static void
John Bowler660c6e42010-12-19 06:22:23 -06002474transform_row(png_structp pp, png_byte buffer[TRANSFORM_ROWMAX],
2475 png_byte colour_type, png_byte bit_depth, png_uint_32 y)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002476{
2477 png_uint_32 v = y << 7;
2478 png_uint_32 i = 0;
2479
2480 switch (bit_size(pp, colour_type, bit_depth))
2481 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002482 case 1:
2483 while (i<128/8) buffer[i] = v & 0xff, v += 17, ++i;
2484 return;
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002485
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002486 case 2:
2487 while (i<128/4) buffer[i] = v & 0xff, v += 33, ++i;
2488 return;
2489
2490 case 4:
2491 while (i<128/2) buffer[i] = v & 0xff, v += 65, ++i;
2492 return;
2493
2494 case 8:
2495 /* 256 bytes total, 128 bytes in each row set as follows: */
2496 while (i<128) buffer[i] = v & 0xff, ++v, ++i;
2497 return;
2498
2499 case 16:
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002500 /* Generate all 65536 pixel values in order, which includes the 8 bit
2501 * GA case as well as the 16 bit G case.
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002502 */
2503 while (i<128)
2504 buffer[2*i] = (v>>8) & 0xff, buffer[2*i+1] = v & 0xff, ++v, ++i;
2505
2506 return;
2507
2508 case 24:
2509 /* 65535 pixels, but rotate the values. */
2510 while (i<128)
2511 {
2512 /* Three bytes per pixel, r, g, b, make b by r^g */
2513 buffer[3*i+0] = (v >> 8) & 0xff;
2514 buffer[3*i+1] = v & 0xff;
2515 buffer[3*i+2] = ((v >> 8) ^ v) & 0xff;
2516 ++v;
2517 ++i;
2518 }
2519
2520 return;
2521
2522 case 32:
2523 /* 65535 pixels, r, g, b, a; just replicate */
2524 while (i<128)
2525 {
2526 buffer[4*i+0] = (v >> 8) & 0xff;
2527 buffer[4*i+1] = v & 0xff;
2528 buffer[4*i+2] = (v >> 8) & 0xff;
2529 buffer[4*i+3] = v & 0xff;
2530 ++v;
2531 ++i;
2532 }
2533
2534 return;
2535
2536 case 48:
2537 /* y is maximum 2047, giving 4x65536 pixels, make 'r' increase by 1 at
2538 * each pixel, g increase by 257 (0x101) and 'b' by 0x1111:
2539 */
2540 while (i<128)
2541 {
2542 png_uint_32 t = v++;
2543 buffer[6*i+0] = (t >> 8) & 0xff;
2544 buffer[6*i+1] = t & 0xff;
2545 t *= 257;
2546 buffer[6*i+2] = (t >> 8) & 0xff;
2547 buffer[6*i+3] = t & 0xff;
2548 t *= 17;
2549 buffer[6*i+4] = (t >> 8) & 0xff;
2550 buffer[6*i+5] = t & 0xff;
2551 ++i;
2552 }
2553
2554 return;
2555
2556 case 64:
2557 /* As above in the 32 bit case. */
2558 while (i<128)
2559 {
2560 png_uint_32 t = v++;
2561 buffer[8*i+0] = (t >> 8) & 0xff;
2562 buffer[8*i+1] = t & 0xff;
2563 buffer[8*i+4] = (t >> 8) & 0xff;
2564 buffer[8*i+5] = t & 0xff;
2565 t *= 257;
2566 buffer[8*i+2] = (t >> 8) & 0xff;
2567 buffer[8*i+3] = t & 0xff;
2568 buffer[8*i+6] = (t >> 8) & 0xff;
2569 buffer[8*i+7] = t & 0xff;
2570 ++i;
2571 }
2572 return;
2573
2574 default:
2575 break;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002576 }
2577
2578 png_error(pp, "internal error");
2579}
2580
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002581/* This is just to do the right cast - could be changed to a function to check
2582 * 'bd' but there isn't much point.
2583 */
2584#define DEPTH(bd) ((png_byte)(1U << (bd)))
2585
John Bowler660c6e42010-12-19 06:22:23 -06002586/* Make a standardized image given a an image colour type, bit depth and
2587 * interlace type. The standard images have a very restricted range of
2588 * rows and heights and are used for testing transforms rather than image
2589 * layout details. See make_size_images below for a way to make images
2590 * that test odd sizes along with the libpng interlace handling.
2591 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002592static void
John Bowler660c6e42010-12-19 06:22:23 -06002593make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
John Bowler9994f252011-05-15 18:52:39 -05002594 png_byte PNG_CONST bit_depth, int palette_number, int interlace_type,
2595 png_const_charp name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002596{
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002597 context(ps, fault);
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05002598
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002599 Try
2600 {
2601 png_infop pi;
2602 png_structp pp = set_store_for_write(ps, &pi, name);
2603 png_uint_32 h;
2604
2605 /* In the event of a problem return control to the Catch statement below
2606 * to do the clean up - it is not possible to 'return' directly from a Try
2607 * block.
2608 */
2609 if (pp == NULL)
2610 Throw ps;
2611
John Bowler660c6e42010-12-19 06:22:23 -06002612 h = transform_height(pp, colour_type, bit_depth);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002613
John Bowler660c6e42010-12-19 06:22:23 -06002614 png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth), h,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002615 bit_depth, colour_type, interlace_type,
2616 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
2617
John Bowlerd273ad22011-05-07 21:00:28 -05002618#ifdef PNG_TEXT_SUPPORTED
2619 {
2620 static char key[] = "image name"; /* must be writeable */
2621 size_t pos;
2622 png_text text;
2623 char copy[FILE_NAME_SIZE];
2624
2625 /* Use a compressed text string to test the correct interaction of text
2626 * compression and IDAT compression.
2627 */
2628 text.compression = PNG_TEXT_COMPRESSION_zTXt;
2629 text.key = key;
2630 /* Yuck: the text must be writable! */
2631 pos = safecat(copy, sizeof copy, 0, ps->wname);
2632 text.text = copy;
2633 text.text_length = pos;
2634 text.itxt_length = 0;
2635 text.lang = 0;
2636 text.lang_key = 0;
2637
2638 png_set_text(pp, pi, &text, 1);
2639 }
2640#endif
2641
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002642 if (colour_type == 3) /* palette */
John Bowler9994f252011-05-15 18:52:39 -05002643 init_standard_palette(ps, pp, pi, 1U << bit_depth, 1/*do tRNS*/);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002644
2645 png_write_info(pp, pi);
2646
2647 if (png_get_rowbytes(pp, pi) !=
John Bowler660c6e42010-12-19 06:22:23 -06002648 transform_rowsize(pp, colour_type, bit_depth))
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002649 png_error(pp, "row size incorrect");
2650
2651 else
2652 {
2653 /* Somewhat confusingly this must be called *after* png_write_info
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002654 * because if it is called before, the information in *pp has not been
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002655 * updated to reflect the interlaced image.
2656 */
2657 int npasses = png_set_interlace_handling(pp);
2658 int pass;
2659
2660 if (npasses != npasses_from_interlace_type(pp, interlace_type))
2661 png_error(pp, "write: png_set_interlace_handling failed");
2662
John Bowler660c6e42010-12-19 06:22:23 -06002663 for (pass=0; pass<npasses; ++pass)
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002664 {
2665 png_uint_32 y;
2666
2667 for (y=0; y<h; ++y)
2668 {
John Bowler660c6e42010-12-19 06:22:23 -06002669 png_byte buffer[TRANSFORM_ROWMAX];
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002670
John Bowler660c6e42010-12-19 06:22:23 -06002671 transform_row(pp, buffer, colour_type, bit_depth, y);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002672 png_write_row(pp, buffer);
2673 }
2674 }
2675 }
2676
John Bowlerd273ad22011-05-07 21:00:28 -05002677#ifdef PNG_TEXT_SUPPORTED
2678 {
2679 static char key[] = "end marker";
2680 static char comment[] = "end";
2681 png_text text;
2682
2683 /* Use a compressed text string to test the correct interaction of text
2684 * compression and IDAT compression.
2685 */
2686 text.compression = PNG_TEXT_COMPRESSION_zTXt;
2687 text.key = key;
2688 text.text = comment;
2689 text.text_length = (sizeof comment)-1;
2690 text.itxt_length = 0;
2691 text.lang = 0;
2692 text.lang_key = 0;
2693
2694 png_set_text(pp, pi, &text, 1);
2695 }
2696#endif
2697
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002698 png_write_end(pp, pi);
2699
2700 /* And store this under the appropriate id, then clean up. */
John Bowler9994f252011-05-15 18:52:39 -05002701 store_storefile(ps, FILEID(colour_type, bit_depth, palette_number,
2702 interlace_type, 0, 0, 0));
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002703
2704 store_write_reset(ps);
2705 }
2706
2707 Catch(fault)
2708 {
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002709 /* Use the png_store returned by the exception. This may help the compiler
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05002710 * because 'ps' is not used in this branch of the setjmp. Note that fault
2711 * and ps will always be the same value.
2712 */
2713 store_write_reset(fault);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002714 }
2715}
2716
2717static void
John Bowler9994f252011-05-15 18:52:39 -05002718make_transform_images(png_store *ps)
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002719{
John Bowler9994f252011-05-15 18:52:39 -05002720 png_byte colour_type = 0;
2721 png_byte bit_depth = 0;
2722 int palette_number = 0;
2723
2724 /* This is in case of errors. */
2725 safecat(ps->test, sizeof ps->test, 0, "make standard images");
2726
2727 /* Use next_format to enumerate all the combinations we test, including
2728 * generating multiple low bit depth palette images.
2729 */
2730 while (next_format(&colour_type, &bit_depth, &palette_number))
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002731 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002732 int interlace_type;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002733
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002734 for (interlace_type = PNG_INTERLACE_NONE;
2735 interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002736 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002737 char name[FILE_NAME_SIZE];
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002738
John Bowler9994f252011-05-15 18:52:39 -05002739 standard_name(name, sizeof name, 0, colour_type, bit_depth,
2740 palette_number, interlace_type, 0, 0, 0);
2741 make_transform_image(ps, colour_type, bit_depth, palette_number,
2742 interlace_type, name);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002743 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002744 }
2745}
2746
John Bowler660c6e42010-12-19 06:22:23 -06002747/* The following two routines use the PNG interlace support macros from
2748 * png.h to interlace or deinterlace rows.
2749 */
2750static void
2751interlace_row(png_bytep buffer, png_const_bytep imageRow,
2752 unsigned int pixel_size, png_uint_32 w, int pass)
2753{
2754 png_uint_32 xin, xout, xstep;
2755
2756 /* Note that this can, trivially, be optimized to a memcpy on pass 7, the
2757 * code is presented this way to make it easier to understand. In practice
2758 * consult the code in the libpng source to see other ways of doing this.
2759 */
2760 xin = PNG_PASS_START_COL(pass);
2761 xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
2762
2763 for (xout=0; xin<w; xin+=xstep)
2764 {
2765 pixel_copy(buffer, xout, imageRow, xin, pixel_size);
2766 ++xout;
2767 }
2768}
2769
2770static void
2771deinterlace_row(png_bytep buffer, png_const_bytep row,
2772 unsigned int pixel_size, png_uint_32 w, int pass)
2773{
2774 /* The inverse of the above, 'row' is part of row 'y' of the output image,
2775 * in 'buffer'. The image is 'w' wide and this is pass 'pass', distribute
2776 * the pixels of row into buffer and return the number written (to allow
2777 * this to be checked).
2778 */
2779 png_uint_32 xin, xout, xstep;
2780
2781 xout = PNG_PASS_START_COL(pass);
2782 xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
2783
2784 for (xin=0; xout<w; xout+=xstep)
2785 {
2786 pixel_copy(buffer, xout, row, xin, pixel_size);
2787 ++xin;
2788 }
2789}
2790
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05002791/* Build a single row for the 'size' test images; this fills in only the
John Bowler660c6e42010-12-19 06:22:23 -06002792 * first bit_width bits of the sample row.
2793 */
2794static void
2795size_row(png_byte buffer[SIZE_ROWMAX], png_uint_32 bit_width, png_uint_32 y)
2796{
2797 /* height is in the range 1 to 16, so: */
2798 y = ((y & 1) << 7) + ((y & 2) << 6) + ((y & 4) << 5) + ((y & 8) << 4);
2799 /* the following ensures bits are set in small images: */
2800 y ^= 0xA5;
2801
2802 while (bit_width >= 8)
2803 *buffer++ = (png_byte)y++, bit_width -= 8;
2804
2805 /* There may be up to 7 remaining bits, these go in the most significant
2806 * bits of the byte.
2807 */
2808 if (bit_width > 0)
2809 {
2810 png_uint_32 mask = (1U<<(8-bit_width))-1;
2811 *buffer = (png_byte)((*buffer & mask) | (y & ~mask));
2812 }
2813}
2814
2815static void
2816make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
2817 png_byte PNG_CONST bit_depth, int PNG_CONST interlace_type,
2818 png_uint_32 PNG_CONST w, png_uint_32 PNG_CONST h,
2819 int PNG_CONST do_interlace)
2820{
John Bowler660c6e42010-12-19 06:22:23 -06002821 context(ps, fault);
2822
John Bowler660c6e42010-12-19 06:22:23 -06002823 Try
2824 {
2825 png_infop pi;
John Bowler56a739b2010-12-19 16:33:20 -06002826 png_structp pp;
John Bowler660c6e42010-12-19 06:22:23 -06002827 unsigned int pixel_size;
John Bowler56a739b2010-12-19 16:33:20 -06002828
2829 /* Make a name and get an appropriate id for the store: */
2830 char name[FILE_NAME_SIZE];
John Bowler9994f252011-05-15 18:52:39 -05002831 PNG_CONST png_uint_32 id = FILEID(colour_type, bit_depth, 0/*palette*/,
2832 interlace_type, w, h, do_interlace);
John Bowler56a739b2010-12-19 16:33:20 -06002833
2834 standard_name_from_id(name, sizeof name, 0, id);
2835 pp = set_store_for_write(ps, &pi, name);
John Bowler660c6e42010-12-19 06:22:23 -06002836
2837 /* In the event of a problem return control to the Catch statement below
2838 * to do the clean up - it is not possible to 'return' directly from a Try
2839 * block.
2840 */
2841 if (pp == NULL)
2842 Throw ps;
2843
2844 png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
2845 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
2846
John Bowler660c6e42010-12-19 06:22:23 -06002847 if (colour_type == 3) /* palette */
John Bowler9994f252011-05-15 18:52:39 -05002848 init_standard_palette(ps, pp, pi, 1U << bit_depth, 0/*do tRNS*/);
John Bowler660c6e42010-12-19 06:22:23 -06002849
2850 png_write_info(pp, pi);
2851
2852 /* Calculate the bit size, divide by 8 to get the byte size - this won't
2853 * overflow because we know the w values are all small enough even for
2854 * a system where 'unsigned int' is only 16 bits.
2855 */
2856 pixel_size = bit_size(pp, colour_type, bit_depth);
2857 if (png_get_rowbytes(pp, pi) != ((w * pixel_size) + 7) / 8)
2858 png_error(pp, "row size incorrect");
2859
2860 else
2861 {
2862 int npasses = npasses_from_interlace_type(pp, interlace_type);
2863 png_uint_32 y;
2864 int pass;
2865 png_byte image[16][SIZE_ROWMAX];
2866
2867 /* To help consistent error detection make the parts of this buffer
2868 * that aren't set below all '1':
2869 */
2870 memset(image, 0xff, sizeof image);
2871
2872 if (!do_interlace && npasses != png_set_interlace_handling(pp))
2873 png_error(pp, "write: png_set_interlace_handling failed");
2874
2875 /* Prepare the whole image first to avoid making it 7 times: */
2876 for (y=0; y<h; ++y)
2877 size_row(image[y], w * pixel_size, y);
2878
2879 for (pass=0; pass<npasses; ++pass)
2880 {
2881 /* The following two are for checking the macros: */
2882 PNG_CONST png_uint_32 wPass = PNG_PASS_COLS(w, pass);
2883
2884 /* If do_interlace is set we don't call png_write_row for every
2885 * row because some of them are empty. In fact, for a 1x1 image,
2886 * most of them are empty!
2887 */
2888 for (y=0; y<h; ++y)
2889 {
2890 png_const_bytep row = image[y];
2891 png_byte tempRow[SIZE_ROWMAX];
2892
2893 /* If do_interlace *and* the image is interlaced we
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05002894 * need a reduced interlace row; this may be reduced
John Bowler660c6e42010-12-19 06:22:23 -06002895 * to empty.
2896 */
2897 if (do_interlace && interlace_type == PNG_INTERLACE_ADAM7)
2898 {
2899 /* The row must not be written if it doesn't exist, notice
2900 * that there are two conditions here, either the row isn't
2901 * ever in the pass or the row would be but isn't wide
2902 * enough to contribute any pixels. In fact the wPass test
2903 * can be used to skip the whole y loop in this case.
2904 */
2905 if (PNG_ROW_IN_INTERLACE_PASS(y, pass) && wPass > 0)
2906 {
2907 /* Set to all 1's for error detection (libpng tends to
2908 * set unset things to 0).
2909 */
2910 memset(tempRow, 0xff, sizeof tempRow);
2911 interlace_row(tempRow, row, pixel_size, w, pass);
2912 row = tempRow;
2913 }
2914 else
2915 continue;
2916 }
2917
2918 /* Only get to here if the row has some pixels in it. */
2919 png_write_row(pp, row);
2920 }
2921 }
2922 }
2923
2924 png_write_end(pp, pi);
2925
2926 /* And store this under the appropriate id, then clean up. */
2927 store_storefile(ps, id);
2928
2929 store_write_reset(ps);
2930 }
2931
2932 Catch(fault)
2933 {
2934 /* Use the png_store returned by the exception. This may help the compiler
2935 * because 'ps' is not used in this branch of the setjmp. Note that fault
2936 * and ps will always be the same value.
2937 */
2938 store_write_reset(fault);
2939 }
2940}
2941
2942static void
2943make_size(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo,
2944 int PNG_CONST bdhi)
2945{
2946 for (; bdlo <= bdhi; ++bdlo)
2947 {
2948 png_uint_32 width;
2949
2950 for (width = 1; width <= 16; ++width)
2951 {
2952 png_uint_32 height;
2953
2954 for (height = 1; height <= 16; ++height)
2955 {
2956 /* The four combinations of DIY interlace and interlace or not -
2957 * no interlace + DIY should be identical to no interlace with
2958 * libpng doing it.
2959 */
2960 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
2961 width, height, 0);
2962 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
2963 width, height, 1);
2964 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
2965 width, height, 0);
2966 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
2967 width, height, 1);
2968 }
2969 }
2970 }
2971}
2972
2973static void
2974make_size_images(png_store *ps)
2975{
2976 /* This is in case of errors. */
2977 safecat(ps->test, sizeof ps->test, 0, "make size images");
2978
2979 /* Arguments are colour_type, low bit depth, high bit depth
2980 */
2981 make_size(ps, 0, 0, WRITE_BDHI);
2982 make_size(ps, 2, 3, WRITE_BDHI);
2983 make_size(ps, 3, 0, 3 /*palette: max 8 bits*/);
2984 make_size(ps, 4, 3, WRITE_BDHI);
2985 make_size(ps, 6, 3, WRITE_BDHI);
2986}
2987
2988/* Return a row based on image id and 'y' for checking: */
2989static void
2990standard_row(png_structp pp, png_byte std[STANDARD_ROWMAX], png_uint_32 id,
2991 png_uint_32 y)
2992{
2993 if (WIDTH_FROM_ID(id) == 0)
2994 transform_row(pp, std, COL_FROM_ID(id), DEPTH_FROM_ID(id), y);
2995 else
2996 size_row(std, WIDTH_FROM_ID(id) * bit_size(pp, COL_FROM_ID(id),
2997 DEPTH_FROM_ID(id)), y);
2998}
2999
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003000/* Tests - individual test cases */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003001/* Like 'make_standard' but errors are deliberately introduced into the calls
3002 * to ensure that they get detected - it should not be possible to write an
3003 * invalid image with libpng!
3004 */
John Bowler88b77cc2011-05-05 06:49:55 -05003005#ifdef PNG_WARNINGS_SUPPORTED
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003006static void
3007sBIT0_error_fn(png_structp pp, png_infop pi)
3008{
3009 /* 0 is invalid... */
3010 png_color_8 bad;
3011 bad.red = bad.green = bad.blue = bad.gray = bad.alpha = 0;
3012 png_set_sBIT(pp, pi, &bad);
3013}
3014
3015static void
3016sBIT_error_fn(png_structp pp, png_infop pi)
3017{
3018 png_byte bit_depth;
3019 png_color_8 bad;
3020
3021 if (png_get_color_type(pp, pi) == PNG_COLOR_TYPE_PALETTE)
3022 bit_depth = 8;
3023
3024 else
3025 bit_depth = png_get_bit_depth(pp, pi);
3026
3027 /* Now we know the bit depth we can easily generate an invalid sBIT entry */
3028 bad.red = bad.green = bad.blue = bad.gray = bad.alpha =
3029 (png_byte)(bit_depth+1);
3030 png_set_sBIT(pp, pi, &bad);
3031}
3032
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003033static PNG_CONST struct
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003034{
3035 void (*fn)(png_structp, png_infop);
3036 PNG_CONST char *msg;
3037 unsigned int warning :1; /* the error is a warning... */
3038} error_test[] =
3039 {
John Bowler88b77cc2011-05-05 06:49:55 -05003040 /* no warnings makes these errors undetectable. */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003041 { sBIT0_error_fn, "sBIT(0): failed to detect error", 1 },
3042 { sBIT_error_fn, "sBIT(too big): failed to detect error", 1 },
3043 };
3044
3045static void
John Bowler56a739b2010-12-19 16:33:20 -06003046make_error(png_store* volatile ps, png_byte PNG_CONST colour_type,
3047 png_byte bit_depth, int interlace_type, int test, png_const_charp name)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003048{
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003049 context(ps, fault);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003050
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003051 Try
3052 {
3053 png_structp pp;
3054 png_infop pi;
3055
3056 pp = set_store_for_write(ps, &pi, name);
3057
3058 if (pp == NULL)
3059 Throw ps;
3060
John Bowler660c6e42010-12-19 06:22:23 -06003061 png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth),
3062 transform_height(pp, colour_type, bit_depth), bit_depth, colour_type,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003063 interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
3064
3065 if (colour_type == 3) /* palette */
John Bowler9994f252011-05-15 18:52:39 -05003066 init_standard_palette(ps, pp, pi, 1U << bit_depth, 0/*do tRNS*/);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003067
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05003068 /* Time for a few errors; these are in various optional chunks, the
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003069 * standard tests test the standard chunks pretty well.
3070 */
John Bowler168a4332011-01-16 19:32:22 -06003071# define exception__prev exception_prev_1
3072# define exception__env exception_env_1
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003073 Try
3074 {
3075 /* Expect this to throw: */
3076 ps->expect_error = !error_test[test].warning;
3077 ps->expect_warning = error_test[test].warning;
3078 ps->saw_warning = 0;
3079 error_test[test].fn(pp, pi);
3080
3081 /* Normally the error is only detected here: */
3082 png_write_info(pp, pi);
3083
3084 /* And handle the case where it was only a warning: */
3085 if (ps->expect_warning && ps->saw_warning)
3086 Throw ps;
3087
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003088 /* If we get here there is a problem, we have success - no error or
3089 * no warning - when we shouldn't have success. Log an error.
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003090 */
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06003091 store_log(ps, pp, error_test[test].msg, 1 /*error*/);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003092 }
3093
3094 Catch (fault)
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003095 ps = fault; /* expected exit, make sure ps is not clobbered */
John Bowler168a4332011-01-16 19:32:22 -06003096#undef exception__prev
3097#undef exception__env
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003098
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003099 /* And clear these flags */
3100 ps->expect_error = 0;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003101 ps->expect_warning = 0;
3102
3103 /* Now write the whole image, just to make sure that the detected, or
3104 * undetected, errro has not created problems inside libpng.
3105 */
3106 if (png_get_rowbytes(pp, pi) !=
John Bowler660c6e42010-12-19 06:22:23 -06003107 transform_rowsize(pp, colour_type, bit_depth))
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003108 png_error(pp, "row size incorrect");
3109
3110 else
3111 {
John Bowler660c6e42010-12-19 06:22:23 -06003112 png_uint_32 h = transform_height(pp, colour_type, bit_depth);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003113 int npasses = png_set_interlace_handling(pp);
3114 int pass;
3115
3116 if (npasses != npasses_from_interlace_type(pp, interlace_type))
3117 png_error(pp, "write: png_set_interlace_handling failed");
3118
John Bowler660c6e42010-12-19 06:22:23 -06003119 for (pass=0; pass<npasses; ++pass)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003120 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003121 png_uint_32 y;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06003122
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003123 for (y=0; y<h; ++y)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003124 {
John Bowler660c6e42010-12-19 06:22:23 -06003125 png_byte buffer[TRANSFORM_ROWMAX];
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003126
John Bowler660c6e42010-12-19 06:22:23 -06003127 transform_row(pp, buffer, colour_type, bit_depth, y);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003128 png_write_row(pp, buffer);
3129 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003130 }
3131 }
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003132
3133 png_write_end(pp, pi);
3134
3135 /* The following deletes the file that was just written. */
3136 store_write_reset(ps);
3137 }
3138
3139 Catch(fault)
3140 {
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003141 store_write_reset(fault);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003142 }
3143}
3144
3145static int
3146make_errors(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
3147 int bdlo, int PNG_CONST bdhi)
3148{
3149 for (; bdlo <= bdhi; ++bdlo)
3150 {
3151 int interlace_type;
3152
3153 for (interlace_type = PNG_INTERLACE_NONE;
3154 interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
3155 {
3156 unsigned int test;
3157 char name[FILE_NAME_SIZE];
3158
John Bowler9994f252011-05-15 18:52:39 -05003159 standard_name(name, sizeof name, 0, colour_type, 1<<bdlo, 0,
3160 interlace_type, 0, 0, 0);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003161
3162 for (test=0; test<(sizeof error_test)/(sizeof error_test[0]); ++test)
3163 {
3164 make_error(&pm->this, colour_type, DEPTH(bdlo), interlace_type,
3165 test, name);
3166
3167 if (fail(pm))
3168 return 0;
3169 }
3170 }
3171 }
3172
3173 return 1; /* keep going */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003174}
John Bowler88b77cc2011-05-05 06:49:55 -05003175#endif
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003176
3177static void
3178perform_error_test(png_modifier *pm)
3179{
John Bowler88b77cc2011-05-05 06:49:55 -05003180#ifdef PNG_WARNINGS_SUPPORTED /* else there are no cases that work! */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003181 /* Need to do this here because we just write in this test. */
3182 safecat(pm->this.test, sizeof pm->this.test, 0, "error test");
3183
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003184 if (!make_errors(pm, 0, 0, WRITE_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003185 return;
3186
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003187 if (!make_errors(pm, 2, 3, WRITE_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003188 return;
3189
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003190 if (!make_errors(pm, 3, 0, 3))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003191 return;
3192
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003193 if (!make_errors(pm, 4, 3, WRITE_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003194 return;
3195
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003196 if (!make_errors(pm, 6, 3, WRITE_BDHI))
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003197 return;
John Bowler88b77cc2011-05-05 06:49:55 -05003198#else
3199 UNUSED(pm)
3200#endif
3201}
3202
3203/* This is just to validate the internal PNG formatting code - if this fails
3204 * then the warning messages the library outputs will probably be garbage.
3205 */
3206static void
3207perform_formatting_test(png_store *volatile ps)
3208{
3209#ifdef PNG_TIME_RFC1123_SUPPORTED
3210 /* The handle into the formatting code is the RFC1123 support; this test does
3211 * nothing if that is compiled out.
3212 */
3213 context(ps, fault);
3214
3215 Try
3216 {
3217 png_const_charp correct = "29 Aug 2079 13:53:60 +0000";
3218 png_const_charp result;
3219 png_structp pp;
3220 png_time pt;
3221
3222 pp = set_store_for_write(ps, NULL, "libpng formatting test");
3223
3224 if (pp == NULL)
3225 Throw ps;
3226
3227
3228 /* Arbitrary settings: */
3229 pt.year = 2079;
3230 pt.month = 8;
3231 pt.day = 29;
3232 pt.hour = 13;
3233 pt.minute = 53;
3234 pt.second = 60; /* a leap second */
3235
3236 result = png_convert_to_rfc1123(pp, &pt);
3237
3238 if (result == NULL)
3239 png_error(pp, "png_convert_to_rfc1123 failed");
3240
3241 if (strcmp(result, correct) != 0)
3242 {
3243 size_t pos = 0;
3244 char msg[128];
3245
3246 pos = safecat(msg, sizeof msg, pos, "png_convert_to_rfc1123(");
3247 pos = safecat(msg, sizeof msg, pos, correct);
3248 pos = safecat(msg, sizeof msg, pos, ") returned: '");
3249 pos = safecat(msg, sizeof msg, pos, result);
3250 pos = safecat(msg, sizeof msg, pos, "'");
3251
3252 png_error(pp, msg);
3253 }
3254
3255 store_write_reset(ps);
3256 }
3257
3258 Catch(fault)
3259 {
3260 store_write_reset(fault);
3261 }
3262#else
3263 UNUSED(ps)
3264#endif
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003265}
3266
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003267/* Because we want to use the same code in both the progressive reader and the
3268 * sequential reader it is necessary to deal with the fact that the progressive
3269 * reader callbacks only have one parameter (png_get_progressive_ptr()), so this
3270 * must contain all the test parameters and all the local variables directly
3271 * accessible to the sequential reader implementation.
3272 *
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003273 * The technique adopted is to reinvent part of what Dijkstra termed a
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003274 * 'display'; an array of pointers to the stack frames of enclosing functions so
3275 * that a nested function definition can access the local (C auto) variables of
3276 * the functions that contain its definition. In fact C provides the first
3277 * pointer (the local variables - the stack frame pointer) and the last (the
3278 * global variables - the BCPL global vector typically implemented as global
3279 * addresses), this code requires one more pointer to make the display - the
3280 * local variables (and function call parameters) of the function that actually
3281 * invokes either the progressive or sequential reader.
3282 *
3283 * Perhaps confusingly this technique is confounded with classes - the
3284 * 'standard_display' defined here is sub-classed as the 'gamma_display' below.
3285 * A gamma_display is a standard_display, taking advantage of the ANSI-C
3286 * requirement that the pointer to the first member of a structure must be the
3287 * same as the pointer to the structure. This allows us to reuse standard_
3288 * functions in the gamma test code; something that could not be done with
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05003289 * nested functions!
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003290 */
3291typedef struct standard_display
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003292{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003293 png_store* ps; /* Test parameters (passed to the function) */
3294 png_byte colour_type;
3295 png_byte bit_depth;
John Bowlerf21a0d02011-01-23 23:55:19 -06003296 png_byte red_sBIT; /* Input data sBIT values. */
3297 png_byte green_sBIT;
3298 png_byte blue_sBIT;
3299 png_byte alpha_sBIT;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003300 int interlace_type;
3301 png_uint_32 id; /* Calculated file ID */
3302 png_uint_32 w; /* Width of image */
3303 png_uint_32 h; /* Height of image */
3304 int npasses; /* Number of interlaced passes */
John Bowler660c6e42010-12-19 06:22:23 -06003305 png_uint_32 pixel_size; /* Width of one pixel in bits */
3306 png_uint_32 bit_width; /* Width of output row in bits */
3307 size_t cbRow; /* Bytes in a row of the output image */
3308 int do_interlace; /* Do interlacing internally */
John Bowler9994f252011-05-15 18:52:39 -05003309 int is_transparent; /* Transparency information was present. */
3310 int speed; /* Doing a speed test */
John Bowlerf21a0d02011-01-23 23:55:19 -06003311 struct
3312 {
3313 png_uint_16 red;
3314 png_uint_16 green;
3315 png_uint_16 blue;
3316 } transparent; /* The transparent color, if set. */
John Bowler9994f252011-05-15 18:52:39 -05003317 int npalette; /* Number of entries in the palette. */
3318 store_palette
John Bowlerafea7d12011-01-28 06:38:14 -06003319 palette;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003320} standard_display;
3321
3322static void
John Bowler660c6e42010-12-19 06:22:23 -06003323standard_display_init(standard_display *dp, png_store* ps, png_uint_32 id,
3324 int do_interlace)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003325{
3326 dp->ps = ps;
John Bowler660c6e42010-12-19 06:22:23 -06003327 dp->colour_type = COL_FROM_ID(id);
3328 dp->bit_depth = DEPTH_FROM_ID(id);
John Bowler9994f252011-05-15 18:52:39 -05003329 if (dp->colour_type == 3)
3330 dp->red_sBIT = dp->blue_sBIT = dp->green_sBIT = dp->alpha_sBIT = 8;
3331 else
3332 dp->red_sBIT = dp->blue_sBIT = dp->green_sBIT = dp->alpha_sBIT =
3333 dp->bit_depth;
John Bowler660c6e42010-12-19 06:22:23 -06003334 dp->interlace_type = INTERLACE_FROM_ID(id);
3335 dp->id = id;
3336 /* All the rest are filled in after the read_info: */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003337 dp->w = 0;
3338 dp->h = 0;
3339 dp->npasses = 0;
John Bowler660c6e42010-12-19 06:22:23 -06003340 dp->pixel_size = 0;
3341 dp->bit_width = 0;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003342 dp->cbRow = 0;
John Bowler660c6e42010-12-19 06:22:23 -06003343 dp->do_interlace = do_interlace;
John Bowlerf21a0d02011-01-23 23:55:19 -06003344 dp->is_transparent = 0;
John Bowler9994f252011-05-15 18:52:39 -05003345 dp->speed = ps->speed;
3346 dp->npalette = 0;
John Bowlerf21a0d02011-01-23 23:55:19 -06003347 /* Preset the transparent color to black: */
3348 memset(&dp->transparent, 0, sizeof dp->transparent);
3349 /* Preset the palette to full intensity/opaque througout: */
3350 memset(dp->palette, 0xff, sizeof dp->palette);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003351}
3352
John Bowler9994f252011-05-15 18:52:39 -05003353/* Initialize the palette fields - this must be done later because the palette
3354 * comes from the particular png_store_file that is selected.
3355 */
3356static void
3357standard_palette_init(standard_display *dp)
3358{
3359 store_palette_entry *palette = store_current_palette(dp->ps, &dp->npalette);
3360
3361 /* The remaining entries remain white/opaque. */
3362 if (dp->npalette > 0)
3363 {
3364 int i = dp->npalette;
3365 memcpy(dp->palette, palette, i * sizeof *palette);
3366
3367 /* Check for a non-opaque palette entry: */
3368 while (--i >= 0)
3369 if (palette[i].alpha < 255)
3370 break;
3371
3372# ifdef __GNUC__
3373 /* GCC can't handle the more obviously optimizable version. */
3374 if (i >= 0)
3375 dp->is_transparent = 1;
3376 else
3377 dp->is_transparent = 0;
3378# else
3379 dp->is_transparent = (i >= 0);
3380# endif
3381 }
3382}
3383
3384/* Utility to read the palette from the PNG file and convert it into
3385 * store_palette format. This returns 1 if there is any transparency in the
3386 * palette (it does not check for a transparent colour in the non-palette case.)
John Bowlerafea7d12011-01-28 06:38:14 -06003387 */
3388static int
John Bowler9994f252011-05-15 18:52:39 -05003389read_palette(store_palette palette, int *npalette, png_structp pp, png_infop pi)
John Bowlerafea7d12011-01-28 06:38:14 -06003390{
3391 png_colorp pal;
3392 png_bytep trans_alpha;
3393 int num;
3394
3395 pal = 0;
John Bowler9994f252011-05-15 18:52:39 -05003396 *npalette = -1;
John Bowlerafea7d12011-01-28 06:38:14 -06003397
John Bowler9994f252011-05-15 18:52:39 -05003398 if (png_get_PLTE(pp, pi, &pal, npalette) & PNG_INFO_PLTE)
3399 {
3400 int i = *npalette;
3401
3402 if (i <= 0 || i > 256)
3403 png_error(pp, "validate: invalid PLTE count");
3404
3405 while (--i >= 0)
John Bowlerafea7d12011-01-28 06:38:14 -06003406 {
3407 palette[i].red = pal[i].red;
3408 palette[i].green = pal[i].green;
3409 palette[i].blue = pal[i].blue;
3410 }
3411
John Bowler9994f252011-05-15 18:52:39 -05003412 /* Mark the remainder of the entries with a flag value (other than
3413 * white/opaque which is the flag value stored above.)
3414 */
3415 memset(palette + *npalette, 126, (256-*npalette) * sizeof *palette);
John Bowlerafea7d12011-01-28 06:38:14 -06003416 }
3417
3418 else /* !png_get_PLTE */
John Bowler9994f252011-05-15 18:52:39 -05003419 {
3420 if (*npalette != (-1))
3421 png_error(pp, "validate: invalid PLTE result");
3422 /* But there is no palette, so record this: */
3423 *npalette = 0;
3424 memset(palette, 113, sizeof palette);
3425 }
John Bowlerafea7d12011-01-28 06:38:14 -06003426
3427 trans_alpha = 0;
John Bowler9994f252011-05-15 18:52:39 -05003428 num = 2; /* force error below */
3429 if ((png_get_tRNS(pp, pi, &trans_alpha, &num, 0) & PNG_INFO_tRNS) != 0 &&
3430 (trans_alpha != NULL || num != 1/*returns 1 for a transparent color*/) &&
3431 /* Oops, if a palette tRNS gets expanded png_read_update_info (at least so
3432 * far as 1.5.3) does not zap the trans_alpha pointer, only num_trans, so
3433 * in the above call we get a success, we get a pointer (who knows what
3434 * to) and we get num_trans == 0:
3435 */
3436 !(trans_alpha != NULL && num == 0)) /* TODO: fix this in libpng. */
John Bowlerafea7d12011-01-28 06:38:14 -06003437 {
3438 int i;
3439
3440 /* Any of these are crash-worthy - given the implementation of
3441 * png_get_tRNS up to 1.5 an app won't crash if it just checks the
3442 * result above and fails to check that the variables it passed have
3443 * actually been filled in! Note that if the app were to pass the
3444 * last, png_color_16p, variable too it couldn't rely on this.
3445 */
John Bowler9994f252011-05-15 18:52:39 -05003446 if (trans_alpha == NULL || num <= 0 || num > 256 || num > *npalette)
John Bowlerafea7d12011-01-28 06:38:14 -06003447 png_error(pp, "validate: unexpected png_get_tRNS (palette) result");
3448
3449 for (i=0; i<num; ++i)
3450 palette[i].alpha = trans_alpha[i];
3451
John Bowler9994f252011-05-15 18:52:39 -05003452 for (num=*npalette; i<num; ++i)
John Bowlerafea7d12011-01-28 06:38:14 -06003453 palette[i].alpha = 255;
3454
John Bowler9994f252011-05-15 18:52:39 -05003455 for (; i<256; ++i)
3456 palette[i].alpha = 33; /* flag value */
3457
John Bowlerafea7d12011-01-28 06:38:14 -06003458 return 1; /* transparency */
3459 }
3460
3461 else
3462 {
John Bowler9994f252011-05-15 18:52:39 -05003463 /* No palette transparency - just set the alpha channel to opaque. */
John Bowlerafea7d12011-01-28 06:38:14 -06003464 int i;
3465
John Bowler9994f252011-05-15 18:52:39 -05003466 for (i=0, num=*npalette; i<num; ++i)
John Bowlerafea7d12011-01-28 06:38:14 -06003467 palette[i].alpha = 255;
3468
John Bowler9994f252011-05-15 18:52:39 -05003469 for (; i<256; ++i)
3470 palette[i].alpha = 55; /* flag value */
3471
John Bowlerafea7d12011-01-28 06:38:14 -06003472 return 0; /* no transparency */
3473 }
3474}
3475
John Bowler9994f252011-05-15 18:52:39 -05003476/* Utility to validate the palette if it should not have changed (the
3477 * non-transform case).
3478 */
3479static void
3480standard_palette_validate(standard_display *dp, png_structp pp, png_infop pi)
3481{
3482 int npalette;
3483 store_palette palette;
3484
3485 if (read_palette(palette, &npalette, pp, pi) != dp->is_transparent)
3486 png_error(pp, "validate: palette transparency changed");
3487
3488 if (npalette != dp->npalette)
3489 {
3490 size_t pos = 0;
3491 char msg[64];
3492
3493 pos = safecat(msg, sizeof msg, pos, "validate: palette size changed: ");
3494 pos = safecatn(msg, sizeof msg, pos, dp->npalette);
3495 pos = safecat(msg, sizeof msg, pos, " -> ");
3496 pos = safecatn(msg, sizeof msg, pos, npalette);
3497 png_error(pp, msg);
3498 }
3499
3500 {
3501 int i = npalette; /* npalette is aliased */
3502
3503 while (--i >= 0)
3504 if (palette[i].red != dp->palette[i].red ||
3505 palette[i].green != dp->palette[i].green ||
3506 palette[i].blue != dp->palette[i].blue ||
3507 palette[i].alpha != dp->palette[i].alpha)
3508 png_error(pp, "validate: PLTE or tRNS chunk changed");
3509 }
3510}
3511
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003512/* By passing a 'standard_display' the progressive callbacks can be used
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003513 * directly by the sequential code, the functions suffixed "_imp" are the
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003514 * implementations, the functions without the suffix are the callbacks.
3515 *
3516 * The code for the info callback is split into two because this callback calls
3517 * png_read_update_info or png_start_read_image and what gets called depends on
3518 * whether the info needs updating (we want to test both calls in pngvalid.)
3519 */
3520static void
3521standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
3522{
3523 if (png_get_bit_depth(pp, pi) != dp->bit_depth)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003524 png_error(pp, "validate: bit depth changed");
3525
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003526 if (png_get_color_type(pp, pi) != dp->colour_type)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003527 png_error(pp, "validate: color type changed");
3528
3529 if (png_get_filter_type(pp, pi) != PNG_FILTER_TYPE_BASE)
3530 png_error(pp, "validate: filter type changed");
3531
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003532 if (png_get_interlace_type(pp, pi) != dp->interlace_type)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003533 png_error(pp, "validate: interlacing changed");
3534
3535 if (png_get_compression_type(pp, pi) != PNG_COMPRESSION_TYPE_BASE)
3536 png_error(pp, "validate: compression type changed");
3537
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003538 dp->w = png_get_image_width(pp, pi);
3539
John Bowler660c6e42010-12-19 06:22:23 -06003540 if (dp->w != standard_width(pp, dp->id))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003541 png_error(pp, "validate: image width changed");
3542
3543 dp->h = png_get_image_height(pp, pi);
3544
John Bowler660c6e42010-12-19 06:22:23 -06003545 if (dp->h != standard_height(pp, dp->id))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003546 png_error(pp, "validate: image height changed");
3547
John Bowlerf21a0d02011-01-23 23:55:19 -06003548 /* Record (but don't check at present) the input sBIT according to the colour
3549 * type information.
3550 */
3551 {
3552 png_color_8p sBIT = 0;
3553
3554 if (png_get_sBIT(pp, pi, &sBIT) & PNG_INFO_sBIT)
3555 {
3556 int sBIT_invalid = 0;
3557
3558 if (sBIT == 0)
3559 png_error(pp, "validate: unexpected png_get_sBIT result");
3560
3561 if (dp->colour_type & PNG_COLOR_MASK_COLOR)
3562 {
3563 if (sBIT->red == 0 || sBIT->red > dp->bit_depth)
3564 sBIT_invalid = 1;
3565 else
3566 dp->red_sBIT = sBIT->red;
3567
3568 if (sBIT->green == 0 || sBIT->green > dp->bit_depth)
3569 sBIT_invalid = 1;
3570 else
3571 dp->green_sBIT = sBIT->green;
3572
3573 if (sBIT->blue == 0 || sBIT->blue > dp->bit_depth)
3574 sBIT_invalid = 1;
3575 else
3576 dp->blue_sBIT = sBIT->blue;
3577 }
3578
3579 else /* !COLOR */
3580 {
3581 if (sBIT->gray == 0 || sBIT->gray > dp->bit_depth)
3582 sBIT_invalid = 1;
3583 else
3584 dp->blue_sBIT = dp->green_sBIT = dp->red_sBIT = sBIT->gray;
3585 }
3586
3587 /* All 8 bits in tRNS for a palette image are significant - see the
3588 * spec.
3589 */
3590 if (dp->colour_type & PNG_COLOR_MASK_ALPHA)
3591 {
3592 if (sBIT->alpha == 0 || sBIT->alpha > dp->bit_depth)
3593 sBIT_invalid = 1;
3594 else
3595 dp->alpha_sBIT = sBIT->alpha;
3596 }
3597
3598 if (sBIT_invalid)
3599 png_error(pp, "validate: sBIT value out of range");
3600 }
3601 }
3602
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003603 /* Important: this is validating the value *before* any transforms have been
3604 * put in place. It doesn't matter for the standard tests, where there are
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003605 * no transforms, but it does for other tests where rowbytes may change after
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003606 * png_read_update_info.
3607 */
John Bowler660c6e42010-12-19 06:22:23 -06003608 if (png_get_rowbytes(pp, pi) != standard_rowsize(pp, dp->id))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003609 png_error(pp, "validate: row size changed");
3610
John Bowler9994f252011-05-15 18:52:39 -05003611 /* Validate the colour type 3 palette (this can be present on other color
3612 * types.)
John Bowlerf21a0d02011-01-23 23:55:19 -06003613 */
John Bowler9994f252011-05-15 18:52:39 -05003614 standard_palette_validate(dp, pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003615
John Bowler9994f252011-05-15 18:52:39 -05003616 /* In any case always check for a tranparent color (notice that the
3617 * colour type 3 case must not give a successful return on the get_tRNS call
3618 * with these arguments!)
3619 */
John Bowlerf21a0d02011-01-23 23:55:19 -06003620 {
3621 png_color_16p trans_color = 0;
3622
3623 if (png_get_tRNS(pp, pi, 0, 0, &trans_color) & PNG_INFO_tRNS)
3624 {
3625 if (trans_color == 0)
3626 png_error(pp, "validate: unexpected png_get_tRNS (color) result");
3627
3628 switch (dp->colour_type)
3629 {
3630 case 0:
3631 dp->transparent.red = dp->transparent.green = dp->transparent.blue =
3632 trans_color->gray;
3633 dp->is_transparent = 1;
3634 break;
3635
3636 case 2:
3637 dp->transparent.red = trans_color->red;
3638 dp->transparent.green = trans_color->green;
3639 dp->transparent.blue = trans_color->blue;
3640 dp->is_transparent = 1;
3641 break;
3642
3643 case 3:
3644 /* Not expected because it should result in the array case
3645 * above.
3646 */
3647 png_error(pp, "validate: unexpected png_get_tRNS result");
3648 break;
3649
3650 default:
3651 png_error(pp, "validate: invalid tRNS chunk with alpha image");
3652 }
3653 }
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003654 }
3655
3656 /* Read the number of passes - expected to match the value used when
3657 * creating the image (interlaced or not). This has the side effect of
John Bowler660c6e42010-12-19 06:22:23 -06003658 * turning on interlace handling (if do_interlace is not set.)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003659 */
John Bowler660c6e42010-12-19 06:22:23 -06003660 dp->npasses = npasses_from_interlace_type(pp, dp->interlace_type);
3661 if (!dp->do_interlace && dp->npasses != png_set_interlace_handling(pp))
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003662 png_error(pp, "validate: file changed interlace type");
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003663
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003664 /* Caller calls png_read_update_info or png_start_read_image now, then calls
3665 * part2.
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003666 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003667}
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003668
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003669/* This must be called *after* the png_read_update_info call to get the correct
3670 * 'rowbytes' value, otherwise png_get_rowbytes will refer to the untransformed
3671 * image.
3672 */
3673static void
3674standard_info_part2(standard_display *dp, png_structp pp, png_infop pi,
3675 int nImages)
3676{
3677 /* Record cbRow now that it can be found. */
John Bowler660c6e42010-12-19 06:22:23 -06003678 dp->pixel_size = bit_size(pp, png_get_color_type(pp, pi),
3679 png_get_bit_depth(pp, pi));
3680 dp->bit_width = png_get_image_width(pp, pi) * dp->pixel_size;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003681 dp->cbRow = png_get_rowbytes(pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003682
John Bowler660c6e42010-12-19 06:22:23 -06003683 /* Validate the rowbytes here again. */
3684 if (dp->cbRow != (dp->bit_width+7)/8)
3685 png_error(pp, "bad png_get_rowbytes calculation");
3686
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003687 /* Then ensure there is enough space for the output image(s). */
John Bowler9994f252011-05-15 18:52:39 -05003688 store_ensure_image(dp->ps, pp, nImages, dp->cbRow, dp->h);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003689}
3690
3691static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003692standard_info_imp(standard_display *dp, png_structp pp, png_infop pi,
3693 int nImages)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003694{
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003695 /* Note that the validation routine has the side effect of turning on
3696 * interlace handling in the subsequent code.
3697 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003698 standard_info_part1(dp, pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003699
3700 /* And the info callback has to call this (or png_read_update_info - see
3701 * below in the png_modifier code for that variant.
3702 */
3703 png_start_read_image(pp);
3704
3705 /* Validate the height, width and rowbytes plus ensure that sufficient buffer
3706 * exists for decoding the image.
3707 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003708 standard_info_part2(dp, pp, pi, nImages);
3709}
3710
3711static void
3712standard_info(png_structp pp, png_infop pi)
3713{
3714 standard_display *dp = png_get_progressive_ptr(pp);
3715
3716 /* Call with nImages==1 because the progressive reader can only produce one
3717 * image.
3718 */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003719 standard_info_imp(dp, pp, pi, 1 /*only one image*/);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003720}
3721
3722static void
3723progressive_row(png_structp pp, png_bytep new_row, png_uint_32 y, int pass)
3724{
John Bowler660c6e42010-12-19 06:22:23 -06003725 PNG_CONST standard_display *dp = png_get_progressive_ptr(pp);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003726
3727 /* When handling interlacing some rows will be absent in each pass, the
John Bowler660c6e42010-12-19 06:22:23 -06003728 * callback still gets called, but with a NULL pointer. This is checked
3729 * in the 'else' clause below. We need our own 'cbRow', but we can't call
3730 * png_get_rowbytes because we got no info structure.
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003731 */
3732 if (new_row != NULL)
3733 {
John Bowler660c6e42010-12-19 06:22:23 -06003734 png_bytep row;
3735
3736 /* In the case where the reader doesn't do the interlace it gives
3737 * us the y in the sub-image:
3738 */
3739 if (dp->do_interlace && dp->interlace_type == PNG_INTERLACE_ADAM7)
John Bowler5432c012011-02-12 08:59:17 -06003740 {
John Bowler4a12f4a2011-04-17 18:34:22 -05003741#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
John Bowler9616ad92011-02-16 06:13:46 -06003742 /* Use this opportunity to validate the png 'current' APIs: */
John Bowler5432c012011-02-12 08:59:17 -06003743 if (y != png_get_current_row_number(pp))
3744 png_error(pp, "png_get_current_row_number is broken");
John Bowler9616ad92011-02-16 06:13:46 -06003745
3746 if (pass != png_get_current_pass_number(pp))
3747 png_error(pp, "png_get_current_pass_number is broken");
John Bowler4a12f4a2011-04-17 18:34:22 -05003748#endif
John Bowler9616ad92011-02-16 06:13:46 -06003749
3750 y = PNG_ROW_FROM_PASS_ROW(y, pass);
John Bowler5432c012011-02-12 08:59:17 -06003751 }
John Bowler660c6e42010-12-19 06:22:23 -06003752
3753 /* Validate this just in case. */
3754 if (y >= dp->h)
3755 png_error(pp, "invalid y to progressive row callback");
3756
John Bowler9994f252011-05-15 18:52:39 -05003757 row = store_image_row(dp->ps, pp, 0, y);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003758
3759 /* Combine the new row into the old: */
John Bowler660c6e42010-12-19 06:22:23 -06003760 if (dp->do_interlace)
3761 {
3762 if (dp->interlace_type == PNG_INTERLACE_ADAM7)
3763 deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass);
3764 else
3765 memcpy(row, new_row, dp->cbRow);
3766 }
3767 else
3768 png_progressive_combine_row(pp, row, new_row);
3769 } else if (dp->interlace_type == PNG_INTERLACE_ADAM7 &&
3770 PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
3771 PNG_PASS_COLS(dp->w, pass) > 0)
3772 png_error(pp, "missing row in progressive de-interlacing");
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003773}
3774
3775static void
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003776sequential_row(standard_display *dp, png_structp pp, png_infop pi,
John Bowler9994f252011-05-15 18:52:39 -05003777 PNG_CONST int iImage, PNG_CONST int iDisplay)
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003778{
3779 PNG_CONST int npasses = dp->npasses;
John Bowler660c6e42010-12-19 06:22:23 -06003780 PNG_CONST int do_interlace = dp->do_interlace &&
3781 dp->interlace_type == PNG_INTERLACE_ADAM7;
3782 PNG_CONST png_uint_32 height = standard_height(pp, dp->id);
3783 PNG_CONST png_uint_32 width = standard_width(pp, dp->id);
John Bowler9994f252011-05-15 18:52:39 -05003784 PNG_CONST png_store* ps = dp->ps;
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003785 int pass;
3786
John Bowler660c6e42010-12-19 06:22:23 -06003787 for (pass=0; pass<npasses; ++pass)
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003788 {
3789 png_uint_32 y;
John Bowler660c6e42010-12-19 06:22:23 -06003790 png_uint_32 wPass = PNG_PASS_COLS(width, pass);
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003791
John Bowler660c6e42010-12-19 06:22:23 -06003792 for (y=0; y<height; ++y)
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003793 {
John Bowler660c6e42010-12-19 06:22:23 -06003794 if (do_interlace)
3795 {
3796 /* wPass may be zero or this row may not be in this pass.
3797 * png_read_row must not be called in either case.
3798 */
3799 if (wPass > 0 && PNG_ROW_IN_INTERLACE_PASS(y, pass))
3800 {
3801 /* Read the row into a pair of temporary buffers, then do the
3802 * merge here into the output rows.
3803 */
3804 png_byte row[STANDARD_ROWMAX], display[STANDARD_ROWMAX];
3805
3806 /* The following aids (to some extent) error detection - we can
3807 * see where png_read_row wrote. Use opposite values in row and
3808 * display to make this easier.
3809 */
3810 memset(row, 0xff, sizeof row);
3811 memset(display, 0, sizeof display);
3812
3813 png_read_row(pp, row, display);
3814
John Bowler9994f252011-05-15 18:52:39 -05003815 if (iImage >= 0)
3816 deinterlace_row(store_image_row(ps, pp, iImage, y), row,
3817 dp->pixel_size, dp->w, pass);
John Bowler660c6e42010-12-19 06:22:23 -06003818
John Bowler9994f252011-05-15 18:52:39 -05003819 if (iDisplay >= 0)
3820 deinterlace_row(store_image_row(ps, pp, iDisplay, y), display,
3821 dp->pixel_size, dp->w, pass);
John Bowler660c6e42010-12-19 06:22:23 -06003822 }
3823 }
3824 else
John Bowler9994f252011-05-15 18:52:39 -05003825 png_read_row(pp,
3826 iImage >= 0 ? store_image_row(ps, pp, iImage, y) : NULL,
3827 iDisplay >= 0 ? store_image_row(ps, pp, iDisplay, y) : NULL);
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003828 }
3829 }
3830
3831 /* And finish the read operation (only really necessary if the caller wants
3832 * to find additional data in png_info from chunks after the last IDAT.)
3833 */
3834 png_read_end(pp, pi);
3835}
3836
3837static void
John Bowler9994f252011-05-15 18:52:39 -05003838standard_row_validate(standard_display *dp, png_structp pp,
3839 int iImage, int iDisplay, png_uint_32 y)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003840{
John Bowler660c6e42010-12-19 06:22:23 -06003841 png_byte std[STANDARD_ROWMAX];
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003842
John Bowler660c6e42010-12-19 06:22:23 -06003843 memset(std, 0xff, sizeof std);
3844 standard_row(pp, std, dp->id, y);
3845
3846 /* At the end both the 'row' and 'display' arrays should end up identical.
3847 * In earlier passes 'row' will be partially filled in, with only the pixels
3848 * that have been read so far, but 'display' will have those pixels
3849 * replicated to fill the unread pixels while reading an interlaced image.
3850 * The side effect inside the libpng sequential reader is that the 'row'
3851 * array retains the correct values for unwritten pixels within the row
3852 * bytes, while the 'display' array gets bits off the end of the image (in
3853 * the last byte) trashed. Unfortunately in the progressive reader the
3854 * row bytes are always trashed, so we always do a pixel_cmp here even though
3855 * a memcmp of all cbRow bytes will succeed for the sequential reader.
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003856 */
John Bowler9994f252011-05-15 18:52:39 -05003857 if (iImage >= 0 && pixel_cmp(std, store_image_row(dp->ps, pp, iImage, y),
3858 dp->bit_width) != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003859 {
3860 char msg[64];
3861 sprintf(msg, "PNG image row %d changed", y);
3862 png_error(pp, msg);
3863 }
3864
John Bowler660c6e42010-12-19 06:22:23 -06003865 /* In this case use pixel_cmp because we need to compare a partial
3866 * byte at the end of the row if the row is not an exact multiple
3867 * of 8 bits wide.
3868 */
John Bowler9994f252011-05-15 18:52:39 -05003869 if (iDisplay >= 0 && pixel_cmp(std, store_image_row(dp->ps, pp, iDisplay, y),
3870 dp->bit_width) != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003871 {
3872 char msg[64];
3873 sprintf(msg, "display row %d changed", y);
3874 png_error(pp, msg);
3875 }
3876}
3877
3878static void
John Bowler9994f252011-05-15 18:52:39 -05003879standard_image_validate(standard_display *dp, png_structp pp, int iImage,
3880 int iDisplay)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003881{
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003882 png_uint_32 y;
3883
John Bowler9994f252011-05-15 18:52:39 -05003884 if (iImage >= 0)
3885 store_image_check(dp->ps, pp, iImage);
3886
3887 if (iDisplay >= 0)
3888 store_image_check(dp->ps, pp, iDisplay);
3889
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003890 for (y=0; y<dp->h; ++y)
John Bowler9994f252011-05-15 18:52:39 -05003891 standard_row_validate(dp, pp, iImage, iDisplay, y);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003892
3893 /* This avoids false positives if the validation code is never called! */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003894 dp->ps->validated = 1;
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003895}
3896
3897static void
3898standard_end(png_structp pp, png_infop pi)
3899{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003900 standard_display *dp = png_get_progressive_ptr(pp);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003901
John Bowlerafea7d12011-01-28 06:38:14 -06003902 UNUSED(pi)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003903
3904 /* Validate the image - progressive reading only produces one variant for
3905 * interlaced images.
3906 */
John Bowler9994f252011-05-15 18:52:39 -05003907 standard_image_validate(dp, pp, 0, -1);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003908}
3909
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003910/* A single test run checking the standard image to ensure it is not damaged. */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003911static void
John Bowler660c6e42010-12-19 06:22:23 -06003912standard_test(png_store* PNG_CONST psIn, png_uint_32 PNG_CONST id,
3913 int do_interlace)
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003914{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003915 standard_display d;
3916 context(psIn, fault);
3917
3918 /* Set up the display (stack frame) variables from the arguments to the
3919 * function and initialize the locals that are filled in later.
3920 */
John Bowler660c6e42010-12-19 06:22:23 -06003921 standard_display_init(&d, psIn, id, do_interlace);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003922
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003923 /* Everything is protected by a Try/Catch. The functions called also
3924 * typically have local Try/Catch blocks.
3925 */
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003926 Try
3927 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003928 png_structp pp;
3929 png_infop pi;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003930
John Bowler660c6e42010-12-19 06:22:23 -06003931 /* Get a png_struct for reading the image. This will throw an error if it
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05003932 * fails, so we don't need to check the result.
3933 */
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003934 pp = set_store_for_read(d.ps, &pi, d.id,
John Bowler660c6e42010-12-19 06:22:23 -06003935 d.do_interlace ? (d.ps->progressive ?
3936 "pngvalid progressive deinterlacer" :
3937 "pngvalid sequential deinterlacer") : (d.ps->progressive ?
3938 "progressive reader" : "sequential reader"));
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003939
John Bowler9994f252011-05-15 18:52:39 -05003940 /* Initialize the palette correctly from the png_store_file. */
3941 standard_palette_init(&d);
3942
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003943 /* Introduce the correct read function. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003944 if (d.ps->progressive)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003945 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003946 png_set_progressive_read_fn(pp, &d, standard_info, progressive_row,
3947 standard_end);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003948
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003949 /* Now feed data into the reader until we reach the end: */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003950 store_progressive_read(d.ps, pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003951 }
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003952 else
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003953 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003954 /* Note that this takes the store, not the display. */
3955 png_set_read_fn(pp, d.ps, store_read);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003956
3957 /* Check the header values: */
3958 png_read_info(pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003959
3960 /* The code tests both versions of the images that the sequential
3961 * reader can produce.
3962 */
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06003963 standard_info_imp(&d, pp, pi, 2 /*images*/);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003964
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003965 /* Need the total bytes in the image below; we can't get to this point
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003966 * unless the PNG file values have been checked against the expected
3967 * values.
3968 */
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003969 {
John Bowler9994f252011-05-15 18:52:39 -05003970 sequential_row(&d, pp, pi, 0, 1);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003971
3972 /* After the last pass loop over the rows again to check that the
3973 * image is correct.
3974 */
John Bowler9994f252011-05-15 18:52:39 -05003975 if (!d.speed)
3976 standard_image_validate(&d, pp, 0, 1);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003977 }
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003978 }
3979
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003980 /* Check for validation. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003981 if (!d.ps->validated)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003982 png_error(pp, "image read failed silently");
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003983
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003984 /* Successful completion. */
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003985 }
3986
3987 Catch(fault)
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003988 d.ps = fault; /* make sure this hasn't been clobbered. */
3989
3990 /* In either case clean up the store. */
3991 store_read_reset(d.ps);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003992}
3993
3994static int
3995test_standard(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003996 int bdlo, int PNG_CONST bdhi)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003997{
3998 for (; bdlo <= bdhi; ++bdlo)
3999 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004000 int interlace_type;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004001
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004002 for (interlace_type = PNG_INTERLACE_NONE;
4003 interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
4004 {
John Bowler9994f252011-05-15 18:52:39 -05004005 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004006 interlace_type, 0, 0, 0), 0/*do_interlace*/);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004007
4008 if (fail(pm))
4009 return 0;
4010 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004011 }
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05004012
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06004013 return 1; /* keep going */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004014}
4015
4016static void
4017perform_standard_test(png_modifier *pm)
4018{
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05004019 /* Test each colour type over the valid range of bit depths (expressed as
4020 * log2(bit_depth) in turn, stop as soon as any error is detected.
4021 */
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05004022 if (!test_standard(pm, 0, 0, READ_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004023 return;
4024
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05004025 if (!test_standard(pm, 2, 3, READ_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004026 return;
4027
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05004028 if (!test_standard(pm, 3, 0, 3))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004029 return;
4030
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05004031 if (!test_standard(pm, 4, 3, READ_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004032 return;
4033
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05004034 if (!test_standard(pm, 6, 3, READ_BDHI))
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05004035 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004036}
4037
4038
John Bowler660c6e42010-12-19 06:22:23 -06004039/********************************** SIZE TESTS ********************************/
4040static int
4041test_size(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
4042 int bdlo, int PNG_CONST bdhi)
4043{
4044 /* Run the tests on each combination.
4045 *
4046 * NOTE: on my 32 bit x86 each of the following blocks takes
4047 * a total of 3.5 seconds if done across every combo of bit depth
4048 * width and height. This is a waste of time in practice, hence the
4049 * hinc and winc stuff:
4050 */
4051 static PNG_CONST png_byte hinc[] = {1, 3, 11, 1, 5};
4052 static PNG_CONST png_byte winc[] = {1, 9, 5, 7, 1};
4053 for (; bdlo <= bdhi; ++bdlo)
4054 {
4055 png_uint_32 h, w;
4056
4057 for (h=1; h<=16; h+=hinc[bdlo]) for (w=1; w<=16; w+=winc[bdlo])
4058 {
4059 /* First test all the 'size' images against the sequential
4060 * reader using libpng to deinterlace (where required.) This
4061 * validates the write side of libpng. There are four possibilities
4062 * to validate.
4063 */
John Bowler9994f252011-05-15 18:52:39 -05004064 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004065 PNG_INTERLACE_NONE, w, h, 0), 0/*do_interlace*/);
4066
4067 if (fail(pm))
4068 return 0;
4069
John Bowler9994f252011-05-15 18:52:39 -05004070 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004071 PNG_INTERLACE_NONE, w, h, 1), 0/*do_interlace*/);
4072
4073 if (fail(pm))
4074 return 0;
4075
John Bowler9994f252011-05-15 18:52:39 -05004076 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004077 PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/);
4078
4079 if (fail(pm))
4080 return 0;
4081
John Bowler9994f252011-05-15 18:52:39 -05004082 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004083 PNG_INTERLACE_ADAM7, w, h, 1), 0/*do_interlace*/);
4084
4085 if (fail(pm))
4086 return 0;
4087
4088 /* Now validate the interlaced read side - do_interlace true,
4089 * in the progressive case this does actually make a difference
4090 * to the code used in the non-interlaced case too.
4091 */
John Bowler9994f252011-05-15 18:52:39 -05004092 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004093 PNG_INTERLACE_NONE, w, h, 0), 1/*do_interlace*/);
4094
4095 if (fail(pm))
4096 return 0;
4097
John Bowler9994f252011-05-15 18:52:39 -05004098 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
John Bowler660c6e42010-12-19 06:22:23 -06004099 PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/);
4100
4101 if (fail(pm))
4102 return 0;
4103 }
4104 }
4105
4106 return 1; /* keep going */
4107}
4108
4109static void
4110perform_size_test(png_modifier *pm)
4111{
4112 /* Test each colour type over the valid range of bit depths (expressed as
4113 * log2(bit_depth) in turn, stop as soon as any error is detected.
4114 */
4115 if (!test_size(pm, 0, 0, READ_BDHI))
4116 return;
4117
4118 if (!test_size(pm, 2, 3, READ_BDHI))
4119 return;
4120
4121 /* For the moment don't do the palette test - it's a waste of time when
4122 * compared to the greyscale test.
4123 */
4124#if 0
4125 if (!test_size(pm, 3, 0, 3))
4126 return;
4127#endif
4128
4129 if (!test_size(pm, 4, 3, READ_BDHI))
4130 return;
4131
4132 if (!test_size(pm, 6, 3, READ_BDHI))
4133 return;
4134}
4135
4136
John Bowlerf21a0d02011-01-23 23:55:19 -06004137/******************************* TRANSFORM TESTS ******************************/
John Bowler4a12f4a2011-04-17 18:34:22 -05004138#ifdef PNG_READ_TRANSFORMS_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06004139/* A set of tests to validate libpng image transforms. The possibilities here
4140 * are legion because the transforms can be combined in a combinatorial
4141 * fashion. To deal with this some measure of restraint is required, otherwise
4142 * the tests would take forever.
4143 */
4144typedef struct image_pixel
4145{
4146 /* A local (pngvalid) representation of a PNG pixel, in all its
4147 * various forms.
4148 */
4149 unsigned int red, green, blue, alpha; /* For non-palette images. */
4150 unsigned int palette_index; /* For a palette image. */
4151 png_byte colour_type; /* As in the spec. */
John Bowlerafea7d12011-01-28 06:38:14 -06004152 png_byte bit_depth; /* Defines bit size in row */
4153 png_byte sample_depth; /* Scale of samples */
John Bowlerf21a0d02011-01-23 23:55:19 -06004154 int have_tRNS; /* tRNS chunk may need processing */
4155
4156 /* For checking the code calculates double precision floating point values
4157 * along with an error value, accumulated from the transforms. Because an
4158 * sBIT setting allows larger error bounds (indeed, by the spec, apparently
4159 * up to just less than +/-1 in the scaled value) the *lowest* sBIT for each
4160 * channel is stored. This sBIT value is folded in to the stored error value
4161 * at the end of the application of the transforms to the pixel.
4162 */
4163 double redf, greenf, bluef, alphaf;
4164 double rede, greene, bluee, alphae;
4165 png_byte red_sBIT, green_sBIT, blue_sBIT, alpha_sBIT;
4166} image_pixel;
4167
4168/* Shared utility function, see below. */
4169static void
4170image_pixel_setf(image_pixel *this, unsigned int max)
4171{
4172 this->redf = this->red / (double)max;
4173 this->greenf = this->green / (double)max;
4174 this->bluef = this->blue / (double)max;
4175 this->alphaf = this->alpha / (double)max;
4176
4177 if (this->red < max)
4178 this->rede = this->redf * DBL_EPSILON;
4179 else
4180 this->rede = 0;
4181 if (this->green < max)
4182 this->greene = this->greenf * DBL_EPSILON;
4183 else
4184 this->greene = 0;
4185 if (this->blue < max)
4186 this->bluee = this->bluef * DBL_EPSILON;
4187 else
4188 this->bluee = 0;
4189 if (this->alpha < max)
4190 this->alphae = this->alphaf * DBL_EPSILON;
4191 else
4192 this->alphae = 0;
4193}
4194
4195/* Initialize the structure for the next pixel - call this before doing any
4196 * transforms and call it for each pixel since all the fields may need to be
4197 * reset.
4198 */
4199static void
4200image_pixel_init(image_pixel *this, png_const_bytep row, png_byte colour_type,
John Bowler9994f252011-05-15 18:52:39 -05004201 png_byte bit_depth, png_uint_32 x, store_palette palette)
John Bowlerf21a0d02011-01-23 23:55:19 -06004202{
John Bowlerafea7d12011-01-28 06:38:14 -06004203 PNG_CONST png_byte sample_depth = (png_byte)(colour_type ==
4204 PNG_COLOR_TYPE_PALETTE ? 8 : bit_depth);
4205 PNG_CONST unsigned int max = (1U<<sample_depth)-1;
John Bowlerf21a0d02011-01-23 23:55:19 -06004206
4207 /* Initially just set everything to the same number and the alpha to opaque.
4208 * Note that this currently assumes a simple palette where entry x has colour
4209 * rgb(x,x,x)!
4210 */
4211 this->palette_index = this->red = this->green = this->blue =
4212 sample(row, colour_type, bit_depth, x, 0);
4213 this->alpha = max;
4214 this->red_sBIT = this->green_sBIT = this->blue_sBIT = this->alpha_sBIT =
John Bowlerafea7d12011-01-28 06:38:14 -06004215 sample_depth;
John Bowlerf21a0d02011-01-23 23:55:19 -06004216
John Bowlerafea7d12011-01-28 06:38:14 -06004217 /* Then override as appropriate: */
4218 if (colour_type == 3) /* palette */
John Bowlerf21a0d02011-01-23 23:55:19 -06004219 {
John Bowlerafea7d12011-01-28 06:38:14 -06004220 /* This permits the caller to default to the sample value. */
4221 if (palette != 0)
4222 {
4223 PNG_CONST unsigned int i = this->palette_index;
4224
4225 this->red = palette[i].red;
4226 this->green = palette[i].green;
4227 this->blue = palette[i].blue;
4228 this->alpha = palette[i].alpha;
4229 }
John Bowlerf21a0d02011-01-23 23:55:19 -06004230 }
John Bowlerafea7d12011-01-28 06:38:14 -06004231
4232 else /* not palette */
4233 {
4234 unsigned int i = 0;
4235
4236 if (colour_type & 2)
4237 {
4238 this->green = sample(row, colour_type, bit_depth, x, 1);
4239 this->blue = sample(row, colour_type, bit_depth, x, 2);
4240 i = 2;
4241 }
4242 if (colour_type & 4)
4243 this->alpha = sample(row, colour_type, bit_depth, x, ++i);
4244 }
John Bowlerf21a0d02011-01-23 23:55:19 -06004245
4246 /* Calculate the scaled values, these are simply the values divided by
4247 * 'max' and the error is initialized to the double precision epsilon value
4248 * from the header file.
4249 */
4250 image_pixel_setf(this, max);
4251
4252 /* Store the input information for use in the transforms - these will
4253 * modify the information.
4254 */
4255 this->colour_type = colour_type;
4256 this->bit_depth = bit_depth;
John Bowlerafea7d12011-01-28 06:38:14 -06004257 this->sample_depth = sample_depth;
John Bowlerf21a0d02011-01-23 23:55:19 -06004258 this->have_tRNS = 0;
4259}
4260
4261/* Convert a palette image to an rgb image. This necessarily converts the tRNS
John Bowler9994f252011-05-15 18:52:39 -05004262 * chunk at the same time, because the tRNS will be in palette form. The way
4263 * palette validation works means that the original palette is never updated,
4264 * instead the image_pixel value from the row contains the RGB of the
4265 * corresponding palette entry and *this* is updated. Consequently this routine
4266 * only needs to change the colour type information.
John Bowlerf21a0d02011-01-23 23:55:19 -06004267 */
4268static void
John Bowler9994f252011-05-15 18:52:39 -05004269image_pixel_convert_PLTE(image_pixel *this)
John Bowlerf21a0d02011-01-23 23:55:19 -06004270{
John Bowlerafea7d12011-01-28 06:38:14 -06004271 if (this->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowlerf21a0d02011-01-23 23:55:19 -06004272 {
John Bowlerf21a0d02011-01-23 23:55:19 -06004273 if (this->have_tRNS)
4274 {
John Bowlerf21a0d02011-01-23 23:55:19 -06004275 this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
4276 this->have_tRNS = 0;
4277 }
4278 else
John Bowlerf21a0d02011-01-23 23:55:19 -06004279 this->colour_type = PNG_COLOR_TYPE_RGB;
John Bowlerf21a0d02011-01-23 23:55:19 -06004280
John Bowler9994f252011-05-15 18:52:39 -05004281 /* The bit depth of the row changes at this point too (notice that this is
4282 * the row format, not the sample depth, which is separate.)
John Bowlerf21a0d02011-01-23 23:55:19 -06004283 */
John Bowler9994f252011-05-15 18:52:39 -05004284 this->bit_depth = 8;
John Bowlerf21a0d02011-01-23 23:55:19 -06004285 }
4286}
4287
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05004288/* Add an alpha channel; this will import the tRNS information because tRNS is
John Bowlerf21a0d02011-01-23 23:55:19 -06004289 * not valid in an alpha image. The bit depth will invariably be set to at
4290 * least 8. Palette images will be converted to alpha (using the above API).
4291 */
4292static void
4293image_pixel_add_alpha(image_pixel *this, const standard_display *display)
4294{
4295 if (this->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05004296 image_pixel_convert_PLTE(this);
John Bowlerf21a0d02011-01-23 23:55:19 -06004297
4298 if ((this->colour_type & PNG_COLOR_MASK_ALPHA) == 0)
4299 {
4300 if (this->colour_type == PNG_COLOR_TYPE_GRAY)
4301 {
4302 if (this->bit_depth < 8)
4303 this->bit_depth = 8;
4304
4305 if (this->have_tRNS)
4306 {
4307 this->have_tRNS = 0;
4308
4309 /* Check the input, original, channel value here against the
4310 * original tRNS gray chunk valie.
4311 */
4312 if (this->red == display->transparent.red)
4313 this->alphaf = 0;
4314 else
4315 this->alphaf = 1;
4316 }
4317 else
4318 this->alphaf = 1;
4319
4320 this->colour_type = PNG_COLOR_TYPE_GRAY_ALPHA;
4321 }
4322
4323 else if (this->colour_type == PNG_COLOR_TYPE_RGB)
4324 {
4325 if (this->have_tRNS)
4326 {
4327 this->have_tRNS = 0;
4328
4329 /* Again, check the exact input values, not the current transformed
4330 * value!
4331 */
4332 if (this->red == display->transparent.red &&
4333 this->green == display->transparent.green &&
4334 this->blue == display->transparent.blue)
4335 this->alphaf = 0;
4336 else
4337 this->alphaf = 1;
4338
4339 this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
4340 }
4341 }
4342
4343 /* The error in the alpha is zero and the sBIT value comes from the
4344 * original sBIT data (actually it will always be the original bit depth).
4345 */
4346 this->alphae = 0;
4347 this->alpha_sBIT = display->alpha_sBIT;
4348 }
4349}
4350
4351struct transform_display;
4352typedef struct image_transform
4353{
John Bowlerafea7d12011-01-28 06:38:14 -06004354 /* The name of this transform: a string. */
4355 PNG_CONST char *name;
4356
4357 /* Each transform can be disabled from the command line: */
4358 int enable;
4359
John Bowlerf21a0d02011-01-23 23:55:19 -06004360 /* The global list of transforms; read only. */
4361 struct image_transform *PNG_CONST list;
4362
4363 /* The global count of the number of times this transform has been set on an
4364 * image.
4365 */
4366 unsigned int global_use;
4367
4368 /* The local count of the number of times this transform has been set. */
4369 unsigned int local_use;
4370
4371 /* The next transform in the list, each transform must call its own next
4372 * transform after it has processed the pixel successfully.
4373 */
4374 PNG_CONST struct image_transform *next;
4375
4376 /* A single transform for the image, expressed as a series of function
4377 * callbacks and some space for values.
4378 *
4379 * First a callback to set the transform on the current png_read_struct:
4380 */
4381 void (*set)(PNG_CONST struct image_transform *this,
4382 struct transform_display *that, png_structp pp, png_infop pi);
4383
4384 /* Then a transform that takes an input pixel in one PNG format or another
4385 * and modifies it by a pngvalid implementation of the transform (thus
4386 * duplicating the libpng intent without, we hope, duplicating the bugs
4387 * in the libpng implementation!) The png_structp is solely to allow error
4388 * reporting via png_error and png_warning.
4389 */
4390 void (*mod)(PNG_CONST struct image_transform *this, image_pixel *that,
4391 png_structp pp, PNG_CONST struct transform_display *display);
4392
4393 /* Add this transform to the list and return true if the transform is
4394 * meaningful for this colour type and bit depth - if false then the
4395 * transform should have no effect on the image so there's not a lot of
4396 * point running it.
4397 */
4398 int (*add)(struct image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06004399 PNG_CONST struct image_transform **that, png_byte colour_type,
John Bowlerf21a0d02011-01-23 23:55:19 -06004400 png_byte bit_depth);
4401} image_transform;
4402
4403typedef struct transform_display
4404{
4405 standard_display this;
4406
4407 /* Parameters */
4408 png_modifier* pm;
4409 PNG_CONST image_transform* transform_list;
4410
4411 /* Local variables */
4412 png_byte output_colour_type;
4413 png_byte output_bit_depth;
John Bowler9994f252011-05-15 18:52:39 -05004414
4415 /* Variables for the individual transforms. */
4416 /* png_set_background */
4417 image_pixel background_colour;
John Bowlerf21a0d02011-01-23 23:55:19 -06004418} transform_display;
4419
4420/* Two functions to end the list: */
4421static void
4422image_transform_set_end(PNG_CONST image_transform *this,
4423 transform_display *that, png_structp pp, png_infop pi)
4424{
John Bowlerafea7d12011-01-28 06:38:14 -06004425 UNUSED(this)
4426 UNUSED(that)
4427 UNUSED(pp)
4428 UNUSED(pi)
John Bowlerf21a0d02011-01-23 23:55:19 -06004429}
4430
4431/* At the end of the list recalculate the output image pixel value from the
4432 * double precision values set up by the preceding 'mod' calls:
4433 */
4434static unsigned int
4435sample_scale(double sample_value, unsigned int scale)
4436{
4437 sample_value = floor(sample_value * scale + .5);
4438
4439 /* Return NaN as 0: */
4440 if (!(sample_value > 0))
4441 sample_value = 0;
4442 else if (sample_value > scale)
4443 sample_value = scale;
4444
4445 return (unsigned int)sample_value;
4446}
4447
4448static void
4449image_transform_mod_end(PNG_CONST image_transform *this, image_pixel *that,
4450 png_structp pp, PNG_CONST transform_display *display)
4451{
John Bowlerafea7d12011-01-28 06:38:14 -06004452 PNG_CONST unsigned int scale = (1U<<that->sample_depth)-1;
John Bowlerf21a0d02011-01-23 23:55:19 -06004453
John Bowlerafea7d12011-01-28 06:38:14 -06004454 UNUSED(this)
4455 UNUSED(pp)
4456 UNUSED(display)
John Bowlerf21a0d02011-01-23 23:55:19 -06004457
John Bowlerafea7d12011-01-28 06:38:14 -06004458 /* At the end recalculate the digitized red green and blue values according
4459 * to the current sample_depth of the pixel.
4460 *
4461 * The sample value is simply scaled to the maximum, checking for over
John Bowlerf21a0d02011-01-23 23:55:19 -06004462 * and underflow (which can both happen for some image transforms,
4463 * including simple size scaling, though libpng doesn't do that at present.
4464 */
4465 that->red = sample_scale(that->redf, scale);
4466
4467 /* The error value is increased, at the end, according to the lowest sBIT
4468 * value seen. Common sense tells us that the intermediate integer
4469 * representations are no more accurate than +/- 0.5 in the integral values,
4470 * the sBIT allows the implementation to be worse than this. In addition the
4471 * PNG specification actually permits any error within the range (-1..+1),
John Bowlerafea7d12011-01-28 06:38:14 -06004472 * but that is ignored here. Instead the final digitized value is compared,
4473 * below to the digitized value of the error limits - this has the net effect
4474 * of allowing (almost) +/-1 in the output value. It's difficult to see how
4475 * any algorithm that digitizes intermediate results can be more accurate.
John Bowlerf21a0d02011-01-23 23:55:19 -06004476 */
4477 that->rede += 1./(2*((1U<<that->red_sBIT)-1));
4478
4479 if (that->colour_type & PNG_COLOR_MASK_COLOR)
4480 {
4481 that->green = sample_scale(that->greenf, scale);
4482 that->blue = sample_scale(that->bluef, scale);
4483 that->greene += 1./(2*((1U<<that->green_sBIT)-1));
4484 that->bluee += 1./(2*((1U<<that->blue_sBIT)-1));
4485 }
4486 else
4487 {
4488 that->blue = that->green = that->red;
4489 that->bluef = that->greenf = that->redf;
4490 that->bluee = that->greene = that->rede;
4491 }
4492
John Bowlerafea7d12011-01-28 06:38:14 -06004493 if ((that->colour_type & PNG_COLOR_MASK_ALPHA) ||
4494 that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowlerf21a0d02011-01-23 23:55:19 -06004495 {
4496 that->alpha = sample_scale(that->alphaf, scale);
4497 that->alphae += 1./(2*((1U<<that->alpha_sBIT)-1));
4498 }
4499 else
4500 {
4501 that->alpha = scale; /* opaque */
4502 that->alpha = 1; /* Override this. */
4503 that->alphae = 0; /* It's exact ;-) */
4504 }
4505}
4506
4507/* Static 'end' structure: */
4508static image_transform image_transform_end =
4509{
John Bowlerafea7d12011-01-28 06:38:14 -06004510 "(end)", /* name */
4511 1, /* enable */
John Bowlerf21a0d02011-01-23 23:55:19 -06004512 0, /* list */
4513 0, /* global_use */
4514 0, /* local_use */
4515 0, /* next */
4516 image_transform_set_end,
4517 image_transform_mod_end,
4518 0 /* never called, I want it to crash if it is! */
4519};
4520
4521/* Reader callbacks and implementations, where they differ from the standard
4522 * ones.
4523 */
4524static void
4525transform_display_init(transform_display *dp, png_modifier *pm, png_uint_32 id,
4526 PNG_CONST image_transform *transform_list)
4527{
4528 /* Standard fields */
4529 standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/);
4530
4531 /* Parameter fields */
4532 dp->pm = pm;
4533 dp->transform_list = transform_list;
4534
4535 /* Local variable fields */
4536 dp->output_colour_type = 255; /* invalid */
4537 dp->output_bit_depth = 255; /* invalid */
4538}
4539
4540static void
4541transform_info_imp(transform_display *dp, png_structp pp, png_infop pi)
4542{
4543 /* Reuse the standard stuff as appropriate. */
4544 standard_info_part1(&dp->this, pp, pi);
4545
4546 /* Now set the list of transforms. */
4547 dp->transform_list->set(dp->transform_list, dp, pp, pi);
4548
4549 /* Update the info structure for these transforms: */
4550 png_read_update_info(pp, pi);
4551
4552 /* And get the output information into the standard_display */
4553 standard_info_part2(&dp->this, pp, pi, 1/*images*/);
4554
4555 /* Plus the extra stuff we need for the transform tests: */
4556 dp->output_colour_type = png_get_color_type(pp, pi);
4557 dp->output_bit_depth = png_get_bit_depth(pp, pi);
4558
4559 /* Validate the combination of colour type and bit depth that we are getting
4560 * out of libpng; the semantics of something not in the PNG spec are, at
4561 * best, unclear.
4562 */
4563 switch (dp->output_colour_type)
4564 {
4565 case PNG_COLOR_TYPE_PALETTE:
4566 if (dp->output_bit_depth > 8) goto error;
4567 /*FALL THROUGH*/
4568 case PNG_COLOR_TYPE_GRAY:
4569 if (dp->output_bit_depth == 1 || dp->output_bit_depth == 2 ||
4570 dp->output_bit_depth == 4)
4571 break;
4572 /*FALL THROUGH*/
4573 default:
4574 if (dp->output_bit_depth == 8 || dp->output_bit_depth == 16)
4575 break;
4576 /*FALL THROUGH*/
4577 error:
4578 {
4579 char message[128];
4580 size_t pos;
4581
4582 pos = safecat(message, sizeof message, 0,
4583 "invalid final bit depth: colour type(");
4584 pos = safecatn(message, sizeof message, pos, dp->output_colour_type);
4585 pos = safecat(message, sizeof message, pos, ") with bit depth: ");
4586 pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
4587
4588 png_error(pp, message);
4589 }
4590 }
4591
4592 /* Use a test pixel to check that the output agrees with what we expect -
4593 * this avoids running the whole test if the output is unexpected.
4594 */
4595 {
4596 image_pixel test_pixel;
4597
4598 memset(&test_pixel, 0, sizeof test_pixel);
4599 test_pixel.colour_type = dp->this.colour_type; /* input */
4600 test_pixel.bit_depth = dp->this.bit_depth;
John Bowlerafea7d12011-01-28 06:38:14 -06004601 if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE)
4602 test_pixel.sample_depth = 8;
4603 else
4604 test_pixel.sample_depth = test_pixel.bit_depth;
John Bowlerf21a0d02011-01-23 23:55:19 -06004605 /* Don't need sBIT here */
4606 test_pixel.have_tRNS = dp->this.is_transparent;
4607
4608 dp->transform_list->mod(dp->transform_list, &test_pixel, pp, dp);
4609
4610 if (test_pixel.colour_type != dp->output_colour_type)
4611 {
4612 char message[128];
4613 size_t pos = safecat(message, sizeof message, 0, "colour type ");
4614
4615 pos = safecatn(message, sizeof message, pos, dp->output_colour_type);
4616 pos = safecat(message, sizeof message, pos, " expected ");
4617 pos = safecatn(message, sizeof message, pos, test_pixel.colour_type);
4618
4619 png_error(pp, message);
John Bowlerf21a0d02011-01-23 23:55:19 -06004620 }
4621
4622 if (test_pixel.bit_depth != dp->output_bit_depth)
4623 {
4624 char message[128];
4625 size_t pos = safecat(message, sizeof message, 0, "bit depth ");
4626
4627 pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
4628 pos = safecat(message, sizeof message, pos, " expected ");
4629 pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth);
4630
4631 png_error(pp, message);
4632 }
John Bowlerafea7d12011-01-28 06:38:14 -06004633
4634 /* If both bit depth and colour type are correct check the sample depth.
4635 * I believe these are both internal errors.
4636 */
4637 if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE)
4638 {
4639 if (test_pixel.sample_depth != 8) /* oops - internal error! */
4640 png_error(pp, "pngvalid: internal: palette sample depth not 8");
4641 }
4642 else if (test_pixel.sample_depth != dp->output_bit_depth)
4643 {
4644 char message[128];
4645 size_t pos = safecat(message, sizeof message, 0,
4646 "internal: sample depth ");
4647
4648 pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
4649 pos = safecat(message, sizeof message, pos, " expected ");
4650 pos = safecatn(message, sizeof message, pos, test_pixel.sample_depth);
4651
4652 png_error(pp, message);
4653 }
John Bowlerf21a0d02011-01-23 23:55:19 -06004654 }
4655}
4656
4657static void
4658transform_info(png_structp pp, png_infop pi)
4659{
4660 transform_info_imp(png_get_progressive_ptr(pp), pp, pi);
4661}
4662
4663static void
John Bowlerafea7d12011-01-28 06:38:14 -06004664transform_range_check(png_structp pp, unsigned int r, unsigned int g,
4665 unsigned int b, unsigned int a, unsigned int in_digitized, double in,
John Bowler5441e182011-05-18 18:57:12 -05004666 unsigned int out, png_byte sample_depth, double err, PNG_CONST char *name,
4667 double digitization_error)
John Bowlerf21a0d02011-01-23 23:55:19 -06004668{
John Bowlerafea7d12011-01-28 06:38:14 -06004669 /* Compare the scaled, digitzed, values of our local calculation (in+-err)
4670 * with the digitized values libpng produced; 'sample_depth' is the actual
4671 * digitization depth of the libpng output colors (the bit depth except for
John Bowler4a12f4a2011-04-17 18:34:22 -05004672 * palette images where it is always 8.) The check on 'err' is to detect
4673 * internal errors in pngvalid itself (the threshold is about 1/255.)
John Bowlerafea7d12011-01-28 06:38:14 -06004674 */
4675 unsigned int max = (1U<<sample_depth)-1;
John Bowler5441e182011-05-18 18:57:12 -05004676 double in_min = ceil((in-err)*max - digitization_error);
4677 double in_max = floor((in+err)*max + digitization_error);
John Bowler4a12f4a2011-04-17 18:34:22 -05004678 if (err > 4E-3 || !(out >= in_min && out <= in_max))
John Bowlerf21a0d02011-01-23 23:55:19 -06004679 {
John Bowlerafea7d12011-01-28 06:38:14 -06004680 char message[256];
John Bowlerf21a0d02011-01-23 23:55:19 -06004681 size_t pos;
4682
4683 pos = safecat(message, sizeof message, 0, name);
John Bowlerafea7d12011-01-28 06:38:14 -06004684 pos = safecat(message, sizeof message, pos, " output value error: rgba(");
4685 pos = safecatn(message, sizeof message, pos, r);
4686 pos = safecat(message, sizeof message, pos, ",");
4687 pos = safecatn(message, sizeof message, pos, g);
4688 pos = safecat(message, sizeof message, pos, ",");
4689 pos = safecatn(message, sizeof message, pos, b);
4690 pos = safecat(message, sizeof message, pos, ",");
4691 pos = safecatn(message, sizeof message, pos, a);
4692 pos = safecat(message, sizeof message, pos, "): ");
4693 pos = safecatn(message, sizeof message, pos, out);
John Bowlerf21a0d02011-01-23 23:55:19 -06004694 pos = safecat(message, sizeof message, pos, " expected: ");
John Bowlerafea7d12011-01-28 06:38:14 -06004695 pos = safecatn(message, sizeof message, pos, in_digitized);
4696 pos = safecat(message, sizeof message, pos, " (");
4697 pos = safecatd(message, sizeof message, pos, (in-err)*max, 3);
John Bowlerf21a0d02011-01-23 23:55:19 -06004698 pos = safecat(message, sizeof message, pos, "..");
John Bowlerafea7d12011-01-28 06:38:14 -06004699 pos = safecatd(message, sizeof message, pos, (in+err)*max, 3);
4700 pos = safecat(message, sizeof message, pos, ")");
John Bowlerf21a0d02011-01-23 23:55:19 -06004701
4702 png_error(pp, message);
4703 }
4704}
4705
4706static void
John Bowler9994f252011-05-15 18:52:39 -05004707transform_image_validate(transform_display *dp, png_structp pp, png_infop pi)
John Bowlerf21a0d02011-01-23 23:55:19 -06004708{
John Bowlerf21a0d02011-01-23 23:55:19 -06004709 /* Constants for the loop below: */
John Bowler9994f252011-05-15 18:52:39 -05004710 PNG_CONST png_store* PNG_CONST ps = dp->this.ps;
John Bowlerf21a0d02011-01-23 23:55:19 -06004711 PNG_CONST png_byte in_ct = dp->this.colour_type;
4712 PNG_CONST png_byte in_bd = dp->this.bit_depth;
4713 PNG_CONST png_uint_32 w = dp->this.w;
4714 PNG_CONST png_uint_32 h = dp->this.h;
John Bowlerf21a0d02011-01-23 23:55:19 -06004715 PNG_CONST png_byte out_ct = dp->output_colour_type;
4716 PNG_CONST png_byte out_bd = dp->output_bit_depth;
John Bowlerafea7d12011-01-28 06:38:14 -06004717 PNG_CONST png_byte sample_depth = (png_byte)(out_ct ==
4718 PNG_COLOR_TYPE_PALETTE ? 8 : out_bd);
John Bowlerf21a0d02011-01-23 23:55:19 -06004719 PNG_CONST png_byte red_sBIT = dp->this.red_sBIT;
4720 PNG_CONST png_byte green_sBIT = dp->this.green_sBIT;
4721 PNG_CONST png_byte blue_sBIT = dp->this.blue_sBIT;
4722 PNG_CONST png_byte alpha_sBIT = dp->this.alpha_sBIT;
4723 PNG_CONST int have_tRNS = dp->this.is_transparent;
John Bowler5441e182011-05-18 18:57:12 -05004724 double digitization_error;
John Bowlerf21a0d02011-01-23 23:55:19 -06004725
John Bowler9994f252011-05-15 18:52:39 -05004726 store_palette out_palette;
John Bowlerf21a0d02011-01-23 23:55:19 -06004727 png_uint_32 y;
4728
John Bowlerafea7d12011-01-28 06:38:14 -06004729 UNUSED(pi)
4730
John Bowler9994f252011-05-15 18:52:39 -05004731 /* Check for row overwrite errors */
4732 store_image_check(dp->this.ps, pp, 0);
4733
John Bowlerafea7d12011-01-28 06:38:14 -06004734 /* Read the palette corresponding to the output if the output colour type
4735 * indicates a palette, othewise set out_palette to garbage.
4736 */
4737 if (out_ct == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05004738 {
4739 /* Validate that the palette count itself has not changed - this is not
4740 * expected.
4741 */
4742 int npalette = (-1);
4743
4744 (void)read_palette(out_palette, &npalette, pp, pi);
4745 if (npalette != dp->this.npalette)
4746 png_error(pp, "unexpected change in palette size");
John Bowler5441e182011-05-18 18:57:12 -05004747
4748 digitization_error = .5;
John Bowler9994f252011-05-15 18:52:39 -05004749 }
John Bowlerafea7d12011-01-28 06:38:14 -06004750 else
John Bowler5441e182011-05-18 18:57:12 -05004751 {
4752 png_byte in_sample_depth;
4753
John Bowlerafea7d12011-01-28 06:38:14 -06004754 memset(out_palette, 0x5e, sizeof out_palette);
4755
John Bowler5441e182011-05-18 18:57:12 -05004756 /* assume-8-bit-calculations means assume that if the input has 8 bit
4757 * (or less) samples and the output has 16 bit samples the calculations
4758 * will be done with 8 bit precision, not 16.
4759 *
4760 * TODO: fix this in libpng; png_set_expand_16 should cause 16 bit
4761 * calculations to be used throughout.
4762 */
4763 if (in_ct == PNG_COLOR_TYPE_PALETTE || in_bd < 16)
4764 in_sample_depth = 8;
4765 else
4766 in_sample_depth = in_bd;
4767
4768 if (sample_depth != 16 || in_sample_depth > 8 ||
4769 !dp->pm->calculations_use_input_precision)
4770 digitization_error = .5;
4771
4772 /* Else errors are at 8 bit precision, scale .5 in 8 bits to the 16 bits:
4773 */
4774 else
4775 digitization_error = .5 * 257;
4776 }
4777
John Bowler9994f252011-05-15 18:52:39 -05004778 for (y=0; y<h; ++y)
John Bowlerf21a0d02011-01-23 23:55:19 -06004779 {
John Bowler9994f252011-05-15 18:52:39 -05004780 png_const_bytep PNG_CONST pRow = store_image_row(ps, pp, 0, y);
John Bowlerf21a0d02011-01-23 23:55:19 -06004781 png_uint_32 x;
4782
4783 /* The original, standard, row pre-transforms. */
4784 png_byte std[STANDARD_ROWMAX];
4785
4786 transform_row(pp, std, in_ct, in_bd, y);
4787
4788 /* Go through each original pixel transforming it and comparing with what
4789 * libpng did to the same pixel.
4790 */
4791 for (x=0; x<w; ++x)
4792 {
4793 image_pixel in_pixel, out_pixel;
John Bowlerafea7d12011-01-28 06:38:14 -06004794 unsigned int r, g, b, a;
John Bowlerf21a0d02011-01-23 23:55:19 -06004795
4796 /* Find out what we think the pixel should be: */
John Bowlerafea7d12011-01-28 06:38:14 -06004797 image_pixel_init(&in_pixel, std, in_ct, in_bd, x, dp->this.palette);
4798
John Bowlerf21a0d02011-01-23 23:55:19 -06004799 in_pixel.red_sBIT = red_sBIT;
4800 in_pixel.green_sBIT = green_sBIT;
4801 in_pixel.blue_sBIT = blue_sBIT;
4802 in_pixel.alpha_sBIT = alpha_sBIT;
4803 in_pixel.have_tRNS = have_tRNS;
4804
John Bowlerafea7d12011-01-28 06:38:14 -06004805 /* For error detection, below. */
4806 r = in_pixel.red;
4807 g = in_pixel.green;
4808 b = in_pixel.blue;
4809 a = in_pixel.alpha;
4810
John Bowlerf21a0d02011-01-23 23:55:19 -06004811 dp->transform_list->mod(dp->transform_list, &in_pixel, pp, dp);
4812
4813 /* Read the output pixel and compare it to what we got, we don't
John Bowlerafea7d12011-01-28 06:38:14 -06004814 * use the error field here, so no need to update sBIT.
John Bowlerf21a0d02011-01-23 23:55:19 -06004815 */
John Bowlerafea7d12011-01-28 06:38:14 -06004816 image_pixel_init(&out_pixel, pRow, out_ct, out_bd, x, out_palette);
John Bowlerf21a0d02011-01-23 23:55:19 -06004817
John Bowlerafea7d12011-01-28 06:38:14 -06004818 /* We don't expect changes to the index here even if the bit depth is
4819 * changed.
4820 */
4821 if (in_ct == PNG_COLOR_TYPE_PALETTE &&
4822 out_ct == PNG_COLOR_TYPE_PALETTE)
John Bowlerf21a0d02011-01-23 23:55:19 -06004823 {
4824 if (in_pixel.palette_index != out_pixel.palette_index)
4825 png_error(pp, "unexpected transformed palette index");
4826 }
4827
John Bowlerafea7d12011-01-28 06:38:14 -06004828 /* Check the colours for palette images too - in fact the palette could
4829 * be separately verified itself in most cases.
4830 */
4831 if (in_pixel.red != out_pixel.red)
4832 transform_range_check(pp, r, g, b, a, in_pixel.red, in_pixel.redf,
John Bowler5441e182011-05-18 18:57:12 -05004833 out_pixel.red, sample_depth, in_pixel.rede, "red/gray",
4834 digitization_error);
John Bowlerf21a0d02011-01-23 23:55:19 -06004835
John Bowlerafea7d12011-01-28 06:38:14 -06004836 if ((out_ct & PNG_COLOR_MASK_COLOR) != 0 &&
4837 in_pixel.green != out_pixel.green)
4838 transform_range_check(pp, r, g, b, a, in_pixel.green,
4839 in_pixel.greenf, out_pixel.green, sample_depth, in_pixel.greene,
John Bowler5441e182011-05-18 18:57:12 -05004840 "green", digitization_error);
John Bowlerf21a0d02011-01-23 23:55:19 -06004841
John Bowlerafea7d12011-01-28 06:38:14 -06004842 if ((out_ct & PNG_COLOR_MASK_COLOR) != 0 &&
4843 in_pixel.blue != out_pixel.blue)
4844 transform_range_check(pp, r, g, b, a, in_pixel.blue, in_pixel.bluef,
John Bowler5441e182011-05-18 18:57:12 -05004845 out_pixel.blue, sample_depth, in_pixel.bluee, "blue",
4846 digitization_error);
John Bowlerf21a0d02011-01-23 23:55:19 -06004847
John Bowlerafea7d12011-01-28 06:38:14 -06004848 if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0 &&
4849 in_pixel.alpha != out_pixel.alpha)
4850 transform_range_check(pp, r, g, b, a, in_pixel.alpha,
4851 in_pixel.alphaf, out_pixel.alpha, sample_depth, in_pixel.alphae,
John Bowler5441e182011-05-18 18:57:12 -05004852 "alpha", digitization_error);
John Bowlerf21a0d02011-01-23 23:55:19 -06004853 } /* pixel (x) loop */
4854 } /* row (y) loop */
4855
4856 /* Record that something was actually checked to avoid a false positive. */
4857 dp->this.ps->validated = 1;
4858}
4859
4860static void
4861transform_end(png_structp pp, png_infop pi)
4862{
4863 transform_display *dp = png_get_progressive_ptr(pp);
4864
John Bowler9994f252011-05-15 18:52:39 -05004865 transform_image_validate(dp, pp, pi);
John Bowlerf21a0d02011-01-23 23:55:19 -06004866}
4867
4868/* A single test run. */
4869static void
4870transform_test(png_modifier *pmIn, PNG_CONST png_uint_32 idIn,
4871 PNG_CONST image_transform* transform_listIn, PNG_CONST char *name)
4872{
4873 transform_display d;
4874 context(&pmIn->this, fault);
4875
4876 transform_display_init(&d, pmIn, idIn, transform_listIn);
4877
4878 Try
4879 {
4880 png_structp pp;
4881 png_infop pi;
4882
John Bowler9994f252011-05-15 18:52:39 -05004883 /* Get a png_struct for reading the image. */
John Bowlerf21a0d02011-01-23 23:55:19 -06004884 pp = set_modifier_for_read(d.pm, &pi, d.this.id, name);
John Bowler9994f252011-05-15 18:52:39 -05004885 standard_palette_init(&d.this);
John Bowlerf21a0d02011-01-23 23:55:19 -06004886
John Bowlerafea7d12011-01-28 06:38:14 -06004887# if 0
4888 /* Logging (debugging only) */
4889 {
4890 char buffer[256];
4891
4892 (void)store_message(&d.pm->this, pp, buffer, sizeof buffer, 0,
4893 "running test");
4894
4895 fprintf(stderr, "%s\n", buffer);
4896 }
4897# endif
4898
John Bowlerf21a0d02011-01-23 23:55:19 -06004899 /* Introduce the correct read function. */
4900 if (d.pm->this.progressive)
4901 {
4902 /* Share the row function with the standard implementation. */
4903 png_set_progressive_read_fn(pp, &d, transform_info, progressive_row,
4904 transform_end);
4905
4906 /* Now feed data into the reader until we reach the end: */
4907 modifier_progressive_read(d.pm, pp, pi);
4908 }
4909 else
4910 {
4911 /* modifier_read expects a png_modifier* */
4912 png_set_read_fn(pp, d.pm, modifier_read);
4913
4914 /* Check the header values: */
4915 png_read_info(pp, pi);
4916
4917 /* Process the 'info' requirements. Only one image is generated */
4918 transform_info_imp(&d, pp, pi);
4919
John Bowler9994f252011-05-15 18:52:39 -05004920 sequential_row(&d.this, pp, pi, -1, 0);
John Bowlerf21a0d02011-01-23 23:55:19 -06004921
John Bowler9994f252011-05-15 18:52:39 -05004922 if (!d.this.speed)
4923 transform_image_validate(&d, pp, pi);
John Bowlerf21a0d02011-01-23 23:55:19 -06004924 }
4925
4926 modifier_reset(d.pm);
4927 }
4928
4929 Catch(fault)
4930 modifier_reset((png_modifier*)fault);
4931}
4932
4933/* The transforms: */
4934#define ITSTRUCT(name) image_transform_##name
John Bowler4a12f4a2011-04-17 18:34:22 -05004935#define IT(name)\
John Bowlerf21a0d02011-01-23 23:55:19 -06004936static image_transform ITSTRUCT(name) =\
4937{\
John Bowlerafea7d12011-01-28 06:38:14 -06004938 #name,\
4939 1, /*enable*/\
John Bowler4a12f4a2011-04-17 18:34:22 -05004940 &PT, /*list*/\
John Bowlerf21a0d02011-01-23 23:55:19 -06004941 0, /*global_use*/\
4942 0, /*local_use*/\
4943 0, /*next*/\
4944 image_transform_png_set_##name##_set,\
4945 image_transform_png_set_##name##_mod,\
4946 image_transform_png_set_##name##_add\
4947}
John Bowler4a12f4a2011-04-17 18:34:22 -05004948#define PT ITSTRUCT(end) /* stores the previous transform */
John Bowlerf21a0d02011-01-23 23:55:19 -06004949
John Bowlerafea7d12011-01-28 06:38:14 -06004950/* To save code: */
John Bowlerf21a0d02011-01-23 23:55:19 -06004951static int
John Bowlerafea7d12011-01-28 06:38:14 -06004952image_transform_default_add(image_transform *this,
4953 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06004954{
John Bowlerafea7d12011-01-28 06:38:14 -06004955 UNUSED(colour_type)
4956 UNUSED(bit_depth)
4957
John Bowlerf21a0d02011-01-23 23:55:19 -06004958 this->next = *that;
4959 *that = this;
4960
John Bowlerafea7d12011-01-28 06:38:14 -06004961 return 1;
John Bowlerf21a0d02011-01-23 23:55:19 -06004962}
4963
John Bowler4a12f4a2011-04-17 18:34:22 -05004964#ifdef PNG_READ_EXPAND_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06004965/* png_set_palette_to_rgb */
4966static void
4967image_transform_png_set_palette_to_rgb_set(PNG_CONST image_transform *this,
4968 transform_display *that, png_structp pp, png_infop pi)
4969{
4970 png_set_palette_to_rgb(pp);
4971 this->next->set(this->next, that, pp, pi);
4972}
4973
4974static void
4975image_transform_png_set_palette_to_rgb_mod(PNG_CONST image_transform *this,
4976 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
4977{
4978 if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05004979 image_pixel_convert_PLTE(that);
John Bowlerf21a0d02011-01-23 23:55:19 -06004980
4981 this->next->mod(this->next, that, pp, display);
4982}
4983
4984static int
4985image_transform_png_set_palette_to_rgb_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06004986 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06004987{
John Bowlerafea7d12011-01-28 06:38:14 -06004988 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06004989
4990 this->next = *that;
4991 *that = this;
4992
John Bowlerf21a0d02011-01-23 23:55:19 -06004993 return colour_type == PNG_COLOR_TYPE_PALETTE;
4994}
4995
John Bowler4a12f4a2011-04-17 18:34:22 -05004996IT(palette_to_rgb);
4997#undef PT
4998#define PT ITSTRUCT(palette_to_rgb)
4999#endif /* PNG_READ_EXPAND_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005000
John Bowler4a12f4a2011-04-17 18:34:22 -05005001#ifdef PNG_READ_EXPAND_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005002/* png_set_tRNS_to_alpha */
5003static void
5004image_transform_png_set_tRNS_to_alpha_set(PNG_CONST image_transform *this,
5005 transform_display *that, png_structp pp, png_infop pi)
5006{
5007 png_set_tRNS_to_alpha(pp);
5008 this->next->set(this->next, that, pp, pi);
5009}
5010
5011static void
5012image_transform_png_set_tRNS_to_alpha_mod(PNG_CONST image_transform *this,
5013 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5014{
John Bowlerafea7d12011-01-28 06:38:14 -06005015 /* LIBPNG BUG: this always forces palette images to RGB. */
5016 if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05005017 image_pixel_convert_PLTE(that);
John Bowlerafea7d12011-01-28 06:38:14 -06005018
John Bowlerf21a0d02011-01-23 23:55:19 -06005019 /* This effectively does an 'expand' only if there is some transparency to
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -05005020 * convert to an alpha channel.
John Bowlerf21a0d02011-01-23 23:55:19 -06005021 */
5022 if (that->have_tRNS)
5023 image_pixel_add_alpha(that, &display->this);
5024
5025 /* LIBPNG BUG: otherwise libpng still expands to 8 bits! */
John Bowlerafea7d12011-01-28 06:38:14 -06005026 else
5027 {
5028 if (that->bit_depth < 8)
5029 that->bit_depth =8;
5030 if (that->sample_depth < 8)
5031 that->sample_depth = 8;
5032 }
John Bowlerf21a0d02011-01-23 23:55:19 -06005033
5034 this->next->mod(this->next, that, pp, display);
5035}
5036
5037static int
5038image_transform_png_set_tRNS_to_alpha_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06005039 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005040{
John Bowlerafea7d12011-01-28 06:38:14 -06005041 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005042
5043 this->next = *that;
5044 *that = this;
5045
John Bowlerf21a0d02011-01-23 23:55:19 -06005046 /* We don't know yet whether there will be a tRNS chunk, but we know that
5047 * this transformation should do nothing if there already is an alpha
5048 * channel.
5049 */
5050 return (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
5051}
5052
John Bowler4a12f4a2011-04-17 18:34:22 -05005053IT(tRNS_to_alpha);
5054#undef PT
5055#define PT ITSTRUCT(tRNS_to_alpha)
5056#endif /* PNG_READ_EXPAND_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005057
John Bowler4a12f4a2011-04-17 18:34:22 -05005058#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005059/* png_set_gray_to_rgb */
5060static void
5061image_transform_png_set_gray_to_rgb_set(PNG_CONST image_transform *this,
5062 transform_display *that, png_structp pp, png_infop pi)
5063{
5064 png_set_gray_to_rgb(pp);
5065 this->next->set(this->next, that, pp, pi);
5066}
5067
5068static void
5069image_transform_png_set_gray_to_rgb_mod(PNG_CONST image_transform *this,
5070 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5071{
5072 /* NOTE: we can actually pend the tRNS processing at this point because we
5073 * can correctly recognize the original pixel value even though we have
5074 * mapped the one gray channel to the three RGB ones, but in fact libpng
5075 * doesn't do this, so we don't either.
5076 */
5077 if ((that->colour_type & PNG_COLOR_MASK_COLOR) == 0 && that->have_tRNS)
5078 image_pixel_add_alpha(that, &display->this);
5079
5080 /* Simply expand the bit depth and alter the colour type as required. */
5081 if (that->colour_type == PNG_COLOR_TYPE_GRAY)
5082 {
5083 /* RGB images have a bit depth at least equal to '8' */
5084 if (that->bit_depth < 8)
John Bowlerafea7d12011-01-28 06:38:14 -06005085 that->sample_depth = that->bit_depth = 8;
John Bowlerf21a0d02011-01-23 23:55:19 -06005086
5087 /* And just changing the colour type works here because the green and blue
5088 * channels are being maintained in lock-step with the red/gray:
5089 */
5090 that->colour_type = PNG_COLOR_TYPE_RGB;
5091 }
5092
5093 else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
5094 that->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
5095
5096 this->next->mod(this->next, that, pp, display);
5097}
5098
5099static int
5100image_transform_png_set_gray_to_rgb_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06005101 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005102{
John Bowlerafea7d12011-01-28 06:38:14 -06005103 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005104
5105 this->next = *that;
5106 *that = this;
5107
John Bowlerf21a0d02011-01-23 23:55:19 -06005108 return (colour_type & PNG_COLOR_MASK_COLOR) == 0;
5109}
5110
John Bowler4a12f4a2011-04-17 18:34:22 -05005111IT(gray_to_rgb);
5112#undef PT
5113#define PT ITSTRUCT(gray_to_rgb)
5114#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005115
John Bowler4a12f4a2011-04-17 18:34:22 -05005116#ifdef PNG_READ_EXPAND_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005117/* png_set_expand */
5118static void
5119image_transform_png_set_expand_set(PNG_CONST image_transform *this,
5120 transform_display *that, png_structp pp, png_infop pi)
5121{
5122 png_set_expand(pp);
5123 this->next->set(this->next, that, pp, pi);
5124}
5125
5126static void
5127image_transform_png_set_expand_mod(PNG_CONST image_transform *this,
5128 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5129{
5130 /* The general expand case depends on what the colour type is: */
John Bowlerf21a0d02011-01-23 23:55:19 -06005131 if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05005132 image_pixel_convert_PLTE(that);
John Bowlerafea7d12011-01-28 06:38:14 -06005133 else if (that->bit_depth < 8) /* grayscale */
5134 that->sample_depth = that->bit_depth = 8;
John Bowlerf21a0d02011-01-23 23:55:19 -06005135
5136 if (that->have_tRNS)
5137 image_pixel_add_alpha(that, &display->this);
5138
5139 this->next->mod(this->next, that, pp, display);
5140}
5141
5142static int
5143image_transform_png_set_expand_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06005144 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005145{
John Bowlerafea7d12011-01-28 06:38:14 -06005146 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005147
5148 this->next = *that;
5149 *that = this;
5150
John Bowlerf21a0d02011-01-23 23:55:19 -06005151 /* 'expand' should do nothing for RGBA or GA input - no tRNS and the bit
5152 * depth is at least 8 already.
5153 */
5154 return (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
5155}
5156
John Bowler4a12f4a2011-04-17 18:34:22 -05005157IT(expand);
5158#undef PT
5159#define PT ITSTRUCT(expand)
5160#endif /* PNG_READ_EXPAND_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005161
John Bowler4a12f4a2011-04-17 18:34:22 -05005162#ifdef PNG_READ_EXPAND_SUPPORTED
John Bowlerafea7d12011-01-28 06:38:14 -06005163/* png_set_expand_gray_1_2_4_to_8
5164 * LIBPNG BUG: this just does an 'expand'
5165 */
5166static void
5167image_transform_png_set_expand_gray_1_2_4_to_8_set(
5168 PNG_CONST image_transform *this, transform_display *that, png_structp pp,
5169 png_infop pi)
5170{
5171 png_set_expand_gray_1_2_4_to_8(pp);
5172 this->next->set(this->next, that, pp, pi);
5173}
5174
5175static void
5176image_transform_png_set_expand_gray_1_2_4_to_8_mod(
5177 PNG_CONST image_transform *this, image_pixel *that, png_structp pp,
5178 PNG_CONST transform_display *display)
5179{
5180 image_transform_png_set_expand_mod(this, that, pp, display);
5181}
5182
5183static int
5184image_transform_png_set_expand_gray_1_2_4_to_8_add(image_transform *this,
5185 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
5186{
5187 return image_transform_png_set_expand_add(this, that, colour_type,
5188 bit_depth);
5189}
5190
John Bowler4a12f4a2011-04-17 18:34:22 -05005191IT(expand_gray_1_2_4_to_8);
5192#undef PT
5193#define PT ITSTRUCT(expand_gray_1_2_4_to_8)
5194#endif /* PNG_READ_EXPAND_SUPPORTED */
5195
5196#ifdef PNG_READ_EXPAND_16_SUPPORTED
John Bowler4d562962011-02-12 09:01:20 -06005197/* png_set_expand_16 */
5198static void
5199image_transform_png_set_expand_16_set(PNG_CONST image_transform *this,
5200 transform_display *that, png_structp pp, png_infop pi)
5201{
5202 png_set_expand_16(pp);
5203 this->next->set(this->next, that, pp, pi);
5204}
5205
5206static void
5207image_transform_png_set_expand_16_mod(PNG_CONST image_transform *this,
5208 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5209{
5210 /* Expect expand_16 to expand everything to 16 bits as a result of also
5211 * causing 'expand' to happen.
5212 */
5213 if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05005214 image_pixel_convert_PLTE(that);
John Bowler4d562962011-02-12 09:01:20 -06005215
5216 if (that->have_tRNS)
5217 image_pixel_add_alpha(that, &display->this);
5218
5219 if (that->bit_depth < 16)
5220 that->sample_depth = that->bit_depth = 16;
5221
5222 this->next->mod(this->next, that, pp, display);
5223}
5224
5225static int
5226image_transform_png_set_expand_16_add(image_transform *this,
5227 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
5228{
5229 UNUSED(colour_type)
5230
5231 this->next = *that;
5232 *that = this;
5233
5234 /* expand_16 does something unless the bit depth is already 16. */
5235 return bit_depth < 16;
5236}
5237
John Bowler4a12f4a2011-04-17 18:34:22 -05005238IT(expand_16);
5239#undef PT
5240#define PT ITSTRUCT(expand_16)
5241#endif /* PNG_READ_EXPAND_16_SUPPORTED */
John Bowlerafea7d12011-01-28 06:38:14 -06005242
John Bowler4a12f4a2011-04-17 18:34:22 -05005243#ifdef PNG_READ_16_TO_8_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005244/* png_set_strip_16 */
5245static void
5246image_transform_png_set_strip_16_set(PNG_CONST image_transform *this,
5247 transform_display *that, png_structp pp, png_infop pi)
5248{
5249 png_set_strip_16(pp);
5250 this->next->set(this->next, that, pp, pi);
5251}
5252
5253static void
5254image_transform_png_set_strip_16_mod(PNG_CONST image_transform *this,
5255 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5256{
5257 if (that->bit_depth == 16)
5258 {
John Bowlerafea7d12011-01-28 06:38:14 -06005259 that->sample_depth = that->bit_depth = 8;
John Bowlerf21a0d02011-01-23 23:55:19 -06005260 if (that->red_sBIT > 8) that->red_sBIT = 8;
5261 if (that->green_sBIT > 8) that->green_sBIT = 8;
5262 if (that->blue_sBIT > 8) that->blue_sBIT = 8;
5263 if (that->alpha_sBIT > 8) that->alpha_sBIT = 8;
5264
5265# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
Glenn Randers-Pehrson97039ba2011-06-13 20:07:27 -05005266 /* The strip 16 algorithm drops the low 8 bits rather than calculating
John Bowlerf21a0d02011-01-23 23:55:19 -06005267 * 1/257, so we need to adjust the permitted errors appropriately:
John Bowler550bab02011-06-14 06:17:26 -05005268 * Notice that this is only relevant prior to the addition of the
5269 * png_set_chop_16 API in 1.5.4 (but 1.5.4+ always defines the above!)
John Bowlerf21a0d02011-01-23 23:55:19 -06005270 */
5271 {
5272 PNG_CONST double d = (255-128.5)/65535;
5273 that->rede += d;
5274 that->greene += d;
5275 that->bluee += d;
5276 that->alphae += d;
5277 }
5278# endif
5279 }
5280
5281 this->next->mod(this->next, that, pp, display);
5282}
5283
5284static int
5285image_transform_png_set_strip_16_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06005286 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005287{
John Bowlerafea7d12011-01-28 06:38:14 -06005288 UNUSED(colour_type)
John Bowlerf21a0d02011-01-23 23:55:19 -06005289
5290 this->next = *that;
5291 *that = this;
5292
John Bowlerf21a0d02011-01-23 23:55:19 -06005293 return bit_depth > 8;
5294}
5295
John Bowler4a12f4a2011-04-17 18:34:22 -05005296IT(strip_16);
5297#undef PT
5298#define PT ITSTRUCT(strip_16)
John Bowler550bab02011-06-14 06:17:26 -05005299
5300#if PNG_LIBPNG_VER >= 10504 /* API added in 1.5.4 */
5301/* png_set_chop_16 */
5302static void
5303image_transform_png_set_chop_16_set(PNG_CONST image_transform *this,
5304 transform_display *that, png_structp pp, png_infop pi)
5305{
5306 png_set_chop_16(pp);
5307 this->next->set(this->next, that, pp, pi);
5308}
5309
5310static void
5311image_transform_png_set_chop_16_mod(PNG_CONST image_transform *this,
5312 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5313{
5314 if (that->bit_depth == 16)
5315 {
5316 that->sample_depth = that->bit_depth = 8;
5317 if (that->red_sBIT > 8) that->red_sBIT = 8;
5318 if (that->green_sBIT > 8) that->green_sBIT = 8;
5319 if (that->blue_sBIT > 8) that->blue_sBIT = 8;
5320 if (that->alpha_sBIT > 8) that->alpha_sBIT = 8;
5321
5322 /* From 1.5.4 there is a separate API to do the low byte drop; see the
5323 * comments above for why this requires the following:
5324 */
5325 {
5326 PNG_CONST double d = (255-128.5)/65535;
5327 that->rede += d;
5328 that->greene += d;
5329 that->bluee += d;
5330 that->alphae += d;
5331 }
5332 }
5333
5334 this->next->mod(this->next, that, pp, display);
5335}
5336
5337static int
5338image_transform_png_set_chop_16_add(image_transform *this,
5339 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
5340{
5341 UNUSED(colour_type)
5342
5343 this->next = *that;
5344 *that = this;
5345
5346 return bit_depth > 8;
5347}
5348
5349IT(chop_16);
5350#undef PT
5351#define PT ITSTRUCT(chop_16)
5352#endif /* From libpng 1.5.4 */
John Bowler4a12f4a2011-04-17 18:34:22 -05005353#endif /* PNG_READ_16_TO_8_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005354
John Bowler4a12f4a2011-04-17 18:34:22 -05005355#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005356/* png_set_strip_alpha */
5357static void
5358image_transform_png_set_strip_alpha_set(PNG_CONST image_transform *this,
5359 transform_display *that, png_structp pp, png_infop pi)
5360{
5361 png_set_strip_alpha(pp);
5362 this->next->set(this->next, that, pp, pi);
5363}
5364
5365static void
5366image_transform_png_set_strip_alpha_mod(PNG_CONST image_transform *this,
5367 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5368{
5369 if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
5370 that->colour_type = PNG_COLOR_TYPE_GRAY;
5371 else if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
5372 that->colour_type = PNG_COLOR_TYPE_RGB;
5373
John Bowlerafea7d12011-01-28 06:38:14 -06005374 that->have_tRNS = 0;
5375 that->alphaf = 1;
John Bowlerafea7d12011-01-28 06:38:14 -06005376
John Bowlerf21a0d02011-01-23 23:55:19 -06005377 this->next->mod(this->next, that, pp, display);
5378}
5379
5380static int
5381image_transform_png_set_strip_alpha_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06005382 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005383{
John Bowlerafea7d12011-01-28 06:38:14 -06005384 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005385
5386 this->next = *that;
5387 *that = this;
5388
John Bowlerf21a0d02011-01-23 23:55:19 -06005389 return (colour_type & PNG_COLOR_MASK_ALPHA) != 0;
5390}
5391
John Bowler4a12f4a2011-04-17 18:34:22 -05005392IT(strip_alpha);
5393#undef PT
5394#define PT ITSTRUCT(strip_alpha)
5395#endif /* PNG_READ_STRIP_ALPHA_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005396
John Bowler4a12f4a2011-04-17 18:34:22 -05005397#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005398/* png_set_rgb_to_gray(png_structp, int err_action, double red, double green)
5399 * png_set_rgb_to_gray_fixed(png_structp, int err_action, png_fixed_point red,
5400 * png_fixed_point green)
5401 * png_get_rgb_to_gray_status
5402 *
5403 * At present the APIs are simply tested using the 16.16 fixed point conversion
5404 * values known to be used inside libpng:
5405 *
5406 * red: 6968
5407 * green: 23434
5408 * blue: 2366
5409 *
5410 * NOTE: this currently ignores the gamma because no gamma is being set, the
5411 * tests on gamma need to happen in the gamma test set.
5412 */
5413static void
5414image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this,
5415 transform_display *that, png_structp pp, png_infop pi)
5416{
5417 PNG_CONST int error_action = 1; /* no error, no defines in png.h */
5418
5419# ifdef PNG_FLOATING_POINT_SUPPORTED
5420 png_set_rgb_to_gray(pp, error_action, -1, -1);
5421# else
5422 png_set_rgb_to_gray_fixed(pp, error_action, -1, -1);
5423# endif
5424
5425 this->next->set(this->next, that, pp, pi);
5426}
5427
5428static void
5429image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
5430 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5431{
5432 if ((that->colour_type & PNG_COLOR_MASK_COLOR) != 0)
5433 {
5434 if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
John Bowler9994f252011-05-15 18:52:39 -05005435 image_pixel_convert_PLTE(that);
John Bowlerf21a0d02011-01-23 23:55:19 -06005436
5437 /* Image now has RGB channels... */
5438 that->bluef = that->greenf = that->redf = (that->redf * 6968 +
5439 that->greenf * 23434 + that->bluef * 2366) / 32768;
5440 that->bluee = that->greene = that->rede = (that->rede * 6968 +
5441 that->greene * 23434 + that->bluee * 2366) / 32768 *
5442 (1 + DBL_EPSILON * 6);
5443
5444 /* The sBIT is the minium of the three colour channel sBITs. */
5445 if (that->red_sBIT > that->green_sBIT)
5446 that->red_sBIT = that->green_sBIT;
5447 if (that->red_sBIT > that->blue_sBIT)
5448 that->red_sBIT = that->blue_sBIT;
5449 that->blue_sBIT = that->green_sBIT = that->red_sBIT;
5450
5451 /* And zap the colour bit in the type: */
5452 if (that->colour_type == PNG_COLOR_TYPE_RGB)
5453 that->colour_type = PNG_COLOR_TYPE_GRAY;
5454 else if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
5455 that->colour_type = PNG_COLOR_TYPE_GRAY_ALPHA;
5456 }
5457
5458 this->next->mod(this->next, that, pp, display);
5459}
5460
5461static int
5462image_transform_png_set_rgb_to_gray_add(image_transform *this,
John Bowlerafea7d12011-01-28 06:38:14 -06005463 PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005464{
John Bowlerafea7d12011-01-28 06:38:14 -06005465 UNUSED(bit_depth)
John Bowlerf21a0d02011-01-23 23:55:19 -06005466
5467 this->next = *that;
5468 *that = this;
5469
John Bowlerf21a0d02011-01-23 23:55:19 -06005470 return (colour_type & PNG_COLOR_MASK_COLOR) != 0;
5471}
5472
John Bowler4a12f4a2011-04-17 18:34:22 -05005473IT(rgb_to_gray);
5474#undef PT
5475#define PT ITSTRUCT(rgb_to_gray)
5476#endif /* PNG_READ_RGB_TO_GRAY_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005477
John Bowler4a12f4a2011-04-17 18:34:22 -05005478#ifdef PNG_READ_BACKGROUND_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06005479/* png_set_background(png_structp, png_const_color_16p background_color,
5480 * int background_gamma_code, int need_expand, double background_gamma)
5481 * png_set_background_fixed(png_structp, png_const_color_16p background_color,
5482 * int background_gamma_code, int need_expand,
5483 * png_fixed_point background_gamma)
5484 *
5485 * As with rgb_to_gray this ignores the gamma.
5486*/
5487static void
5488image_transform_png_set_background_set(PNG_CONST image_transform *this,
5489 transform_display *that, png_structp pp, png_infop pi)
5490{
John Bowler9994f252011-05-15 18:52:39 -05005491 png_byte colour_type, bit_depth;
5492 png_byte random_bytes[8]; /* 8 bytes - 64 bits - the biggest pixel */
John Bowlerf21a0d02011-01-23 23:55:19 -06005493 png_color_16 back;
5494
John Bowler9994f252011-05-15 18:52:39 -05005495 /* We need a background colour, because we don't know exactly what transforms
5496 * have been set we have to supply the colour in the original file format and
5497 * so we need to know what that is! The background colour is stored in the
5498 * transform_display.
John Bowlerafea7d12011-01-28 06:38:14 -06005499 */
John Bowler9994f252011-05-15 18:52:39 -05005500 RANDOMIZE(random_bytes);
John Bowlerf21a0d02011-01-23 23:55:19 -06005501
John Bowler9994f252011-05-15 18:52:39 -05005502 /* Read the random value, for colour type 3 the background colour is actually
5503 * expressed as a 24bit rgb, not an index.
5504 */
5505 colour_type = that->this.colour_type;
5506 if (colour_type == 3)
5507 {
5508 colour_type = PNG_COLOR_TYPE_RGB;
5509 bit_depth = 8;
5510 }
5511
5512 else
5513 bit_depth = that->this.bit_depth;
5514
5515 image_pixel_init(&that->background_colour, random_bytes, colour_type,
5516 bit_depth, 0/*x*/, 0/*unused: palette*/);
5517
5518 /* Extract the background colour from this image_pixel, but make sure the
5519 * unused fields of 'back' are garbage.
5520 */
5521 RANDOMIZE(back);
5522
5523 if (colour_type & PNG_COLOR_MASK_COLOR)
5524 {
5525 back.red = (png_uint_16)that->background_colour.red;
5526 back.green = (png_uint_16)that->background_colour.green;
5527 back.blue = (png_uint_16)that->background_colour.blue;
5528 }
5529
5530 else
5531 back.gray = (png_uint_16)that->background_colour.red;
Glenn Randers-Pehrson0e128df2011-05-15 19:09:24 -05005532
John Bowlerf21a0d02011-01-23 23:55:19 -06005533# ifdef PNG_FLOATING_POINT_SUPPORTED
John Bowler9994f252011-05-15 18:52:39 -05005534 png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, 1/*need expand*/,
5535 0);
John Bowlerf21a0d02011-01-23 23:55:19 -06005536# else
John Bowler9994f252011-05-15 18:52:39 -05005537 png_set_background_fixed(pp, &back, PNG_BACKGROUND_GAMMA_FILE,
5538 1/*need expand*/, 0);
John Bowlerf21a0d02011-01-23 23:55:19 -06005539# endif
5540
5541 this->next->set(this->next, that, pp, pi);
5542}
5543
5544static void
5545image_transform_png_set_background_mod(PNG_CONST image_transform *this,
5546 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5547{
John Bowlerafea7d12011-01-28 06:38:14 -06005548 /* Check for tRNS first: */
5549 if (that->have_tRNS && that->colour_type != PNG_COLOR_TYPE_PALETTE)
John Bowlerf21a0d02011-01-23 23:55:19 -06005550 image_pixel_add_alpha(that, &display->this);
5551
John Bowlerafea7d12011-01-28 06:38:14 -06005552 /* This is only necessary if the alpha value is less than 1. */
5553 if (that->alphaf < 1)
John Bowlerf21a0d02011-01-23 23:55:19 -06005554 {
John Bowler9994f252011-05-15 18:52:39 -05005555 PNG_CONST image_pixel *back = &display->background_colour;
John Bowlerf21a0d02011-01-23 23:55:19 -06005556
John Bowlerafea7d12011-01-28 06:38:14 -06005557 /* Now we do the background calculation without any gamma correction. */
John Bowlerf21a0d02011-01-23 23:55:19 -06005558 if (that->alphaf <= 0)
5559 {
John Bowler9994f252011-05-15 18:52:39 -05005560 that->redf = back->redf;
5561 that->greenf = back->greenf;
5562 that->bluef = back->bluef;
5563
5564 that->rede = back->rede;
5565 that->greene = back->greene;
5566 that->bluee = back->bluee;
5567
5568 that->red_sBIT= back->red_sBIT;
5569 that->green_sBIT= back->green_sBIT;
5570 that->blue_sBIT= back->blue_sBIT;
John Bowlerf21a0d02011-01-23 23:55:19 -06005571 }
5572
John Bowler9994f252011-05-15 18:52:39 -05005573 else /* 0 < alpha < 1 */
John Bowlerf21a0d02011-01-23 23:55:19 -06005574 {
John Bowler9994f252011-05-15 18:52:39 -05005575 double alf = 1 - that->alphaf;
5576
5577 that->redf = that->redf * that->alphaf + back->redf * alf;
5578 that->rede = that->rede * that->alphaf + back->rede * alf +
John Bowlerf21a0d02011-01-23 23:55:19 -06005579 DBL_EPSILON;
John Bowler9994f252011-05-15 18:52:39 -05005580 that->greenf = that->greenf * that->alphaf + back->greenf * alf;
5581 that->greene = that->greene * that->alphaf + back->greene * alf +
5582 DBL_EPSILON;
5583 that->bluef = that->bluef * that->alphaf + back->bluef * alf;
5584 that->bluee = that->bluee * that->alphaf + back->bluee * alf +
John Bowlerf21a0d02011-01-23 23:55:19 -06005585 DBL_EPSILON;
5586 }
5587
John Bowler9994f252011-05-15 18:52:39 -05005588 /* Remove the alpha type and set the alpha (not in that order.) */
John Bowlerafea7d12011-01-28 06:38:14 -06005589 that->alphaf = 1;
5590 that->alphae = 0;
John Bowlerf21a0d02011-01-23 23:55:19 -06005591
John Bowlerafea7d12011-01-28 06:38:14 -06005592 if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
5593 that->colour_type = PNG_COLOR_TYPE_RGB;
5594 else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
5595 that->colour_type = PNG_COLOR_TYPE_GRAY;
John Bowler9994f252011-05-15 18:52:39 -05005596 /* PNG_COLOR_TYPE_PALETTE is not changed */
John Bowlerf21a0d02011-01-23 23:55:19 -06005597 }
5598
5599 this->next->mod(this->next, that, pp, display);
5600}
5601
John Bowlerafea7d12011-01-28 06:38:14 -06005602#define image_transform_png_set_background_add image_transform_default_add
John Bowlerf21a0d02011-01-23 23:55:19 -06005603
John Bowler4a12f4a2011-04-17 18:34:22 -05005604IT(background);
5605#undef PT
5606#define PT ITSTRUCT(background)
5607#endif /* PNG_READ_BACKGROUND_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005608
John Bowler4a12f4a2011-04-17 18:34:22 -05005609/* This may just be 'end' if all the transforms are disabled! */
5610static image_transform *PNG_CONST image_transform_first = &PT;
John Bowlerf21a0d02011-01-23 23:55:19 -06005611
John Bowlerafea7d12011-01-28 06:38:14 -06005612static void
5613transform_enable(PNG_CONST char *name)
John Bowlerf21a0d02011-01-23 23:55:19 -06005614{
John Bowlerafea7d12011-01-28 06:38:14 -06005615 /* Everything starts out enabled, so if we see an 'enable' disabled
5616 * everything else the first time round.
5617 */
5618 static int all_disabled = 0;
5619 int found_it = 0;
5620 image_transform *list = image_transform_first;
5621
5622 while (list != &image_transform_end)
5623 {
5624 if (strcmp(list->name, name) == 0)
5625 {
5626 list->enable = 1;
5627 found_it = 1;
5628 }
5629 else if (!all_disabled)
5630 list->enable = 0;
5631
5632 list = list->list;
5633 }
5634
5635 all_disabled = 1;
5636
5637 if (!found_it)
5638 {
5639 fprintf(stderr, "pngvalid: --transform-enable=%s: unknown transform\n",
5640 name);
5641 exit(1);
5642 }
5643}
5644
5645static void
5646transform_disable(PNG_CONST char *name)
5647{
5648 image_transform *list = image_transform_first;
5649
5650 while (list != &image_transform_end)
5651 {
5652 if (strcmp(list->name, name) == 0)
5653 {
5654 list->enable = 0;
5655 return;
5656 }
5657
5658 list = list->list;
5659 }
5660
5661 fprintf(stderr, "pngvalid: --transform-disable=%s: unknown transform\n",
5662 name);
5663 exit(1);
5664}
5665
5666static void
5667image_transform_reset_count(void)
5668{
5669 image_transform *next = image_transform_first;
5670 int count = 0;
5671
5672 while (next != &image_transform_end)
5673 {
5674 next->local_use = 0;
5675 next->next = 0;
5676 next = next->list;
5677 ++count;
5678 }
5679
5680 /* This can only happen if we every have more than 32 transforms (excluding
5681 * the end) in the list.
5682 */
5683 if (count > 32) abort();
5684}
5685
5686static int
5687image_transform_test_counter(png_uint_32 counter, unsigned int max)
5688{
5689 /* Test the list to see if there is any point contining, given a current
5690 * counter and a 'max' value.
5691 */
5692 image_transform *next = image_transform_first;
5693
5694 while (next != &image_transform_end)
5695 {
5696 /* For max 0 or 1 continue until the counter overflows: */
5697 counter >>= 1;
5698
5699 /* Continue if any entry hasn't reacked the max. */
5700 if (max > 1 && next->local_use < max)
5701 return 1;
5702 next = next->list;
5703 }
5704
5705 return max <= 1 && counter == 0;
5706}
John Bowlerf21a0d02011-01-23 23:55:19 -06005707
5708static png_uint_32
John Bowlerafea7d12011-01-28 06:38:14 -06005709image_transform_add(PNG_CONST image_transform **this, unsigned int max,
John Bowlerf21a0d02011-01-23 23:55:19 -06005710 png_uint_32 counter, char *name, size_t sizeof_name, size_t *pos,
5711 png_byte colour_type, png_byte bit_depth)
5712{
John Bowlerafea7d12011-01-28 06:38:14 -06005713 for (;;) /* until we manage to add something */
John Bowlerf21a0d02011-01-23 23:55:19 -06005714 {
John Bowlerafea7d12011-01-28 06:38:14 -06005715 png_uint_32 mask;
5716 image_transform *list;
John Bowlerf21a0d02011-01-23 23:55:19 -06005717
John Bowlerafea7d12011-01-28 06:38:14 -06005718 /* Find the next counter value, if the counter is zero this is the start
5719 * of the list. This routine always returns the current counter (not the
5720 * next) so it returns 0 at the end and expects 0 at the beginning.
5721 */
5722 if (counter == 0) /* first time */
John Bowlerf21a0d02011-01-23 23:55:19 -06005723 {
John Bowlerafea7d12011-01-28 06:38:14 -06005724 image_transform_reset_count();
5725 if (max <= 1)
5726 counter = 1;
5727 else
5728 counter = random_32();
5729 }
5730 else /* advance the counter */
5731 {
5732 switch (max)
John Bowlerf21a0d02011-01-23 23:55:19 -06005733 {
John Bowlerafea7d12011-01-28 06:38:14 -06005734 case 0: ++counter; break;
5735 case 1: counter <<= 1; break;
5736 default: counter = random_32(); break;
John Bowlerf21a0d02011-01-23 23:55:19 -06005737 }
5738 }
5739
John Bowlerafea7d12011-01-28 06:38:14 -06005740 /* Now add all these items, if possible */
5741 *this = &image_transform_end;
5742 list = image_transform_first;
5743 mask = 1;
John Bowlerf21a0d02011-01-23 23:55:19 -06005744
John Bowlerafea7d12011-01-28 06:38:14 -06005745 /* Go through the whole list adding anything that the counter selects: */
5746 while (list != &image_transform_end)
5747 {
5748 if ((counter & mask) != 0 && list->enable &&
5749 (max == 0 || list->local_use < max))
5750 {
5751 /* Candidate to add: */
5752 if (list->add(list, this, colour_type, bit_depth) || max == 0)
5753 {
5754 /* Added, so add to the name too. */
5755 *pos = safecat(name, sizeof_name, *pos, " +");
5756 *pos = safecat(name, sizeof_name, *pos, list->name);
5757 }
John Bowlerf21a0d02011-01-23 23:55:19 -06005758
John Bowlerafea7d12011-01-28 06:38:14 -06005759 else
5760 {
5761 /* Not useful and max>0, so remvoe it from *this: */
5762 *this = list->next;
5763 list->next = 0;
John Bowlerf21a0d02011-01-23 23:55:19 -06005764
John Bowlerafea7d12011-01-28 06:38:14 -06005765 /* And, since we know it isn't useful, stop it being added again
5766 * in this run:
5767 */
5768 list->local_use = max;
5769 }
5770 }
John Bowlerf21a0d02011-01-23 23:55:19 -06005771
John Bowlerafea7d12011-01-28 06:38:14 -06005772 mask <<= 1;
5773 list = list->list;
5774 }
John Bowlerf21a0d02011-01-23 23:55:19 -06005775
John Bowlerafea7d12011-01-28 06:38:14 -06005776 /* Now if anything was added we have something to do. */
5777 if (*this != &image_transform_end)
5778 return counter;
5779
5780 /* Nothing added, but was there anything in there to add? */
5781 if (!image_transform_test_counter(counter, max))
5782 return 0;
John Bowlerf21a0d02011-01-23 23:55:19 -06005783 }
5784}
5785
5786#ifdef THIS_IS_THE_PROFORMA
5787static void
5788image_transform_png_set_@_set(PNG_CONST image_transform *this,
5789 transform_display *that, png_structp pp, png_infop pi)
5790{
5791 png_set_@(pp);
5792 this->next->set(this->next, that, pp, pi);
5793}
5794
5795static void
5796image_transform_png_set_@_mod(PNG_CONST image_transform *this,
5797 image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
5798{
5799 this->next->mod(this->next, that, pp, display);
5800}
5801
5802static int
5803image_transform_png_set_@_add(image_transform *this,
5804 PNG_CONST image_transform **that, char *name, size_t sizeof_name,
5805 size_t *pos, png_byte colour_type, png_byte bit_depth)
5806{
5807 this->next = *that;
5808 *that = this;
5809
5810 *pos = safecat(name, sizeof_name, *pos, " +@");
5811
5812 return 1;
5813}
5814
5815IT(@);
5816#endif
5817
5818/* png_set_quantize(png_structp, png_colorp palette, int num_palette,
5819 * int maximum_colors, png_const_uint_16p histogram, int full_quantize)
5820 *
5821 * Very difficult to validate this!
5822 */
5823/*NOTE: TBD NYI */
5824
5825/* The data layout transforms are handled by swapping our own channel data,
5826 * necessarily these need to happen at the end of the transform list because the
5827 * semantic of the channels changes after these are executed. Some of these,
5828 * like set_shift and set_packing, can't be done at present because they change
5829 * the layout of the data at the sub-sample level so sample() won't get the
5830 * right answer.
5831 */
5832/* png_set_invert_alpha */
5833/*NOTE: TBD NYI */
5834
5835/* png_set_bgr */
5836/*NOTE: TBD NYI */
5837
5838/* png_set_swap_alpha */
5839/*NOTE: TBD NYI */
5840
5841/* png_set_swap */
5842/*NOTE: TBD NYI */
5843
5844/* png_set_filler, (png_structp png_ptr, png_uint_32 filler, int flags)); */
5845/*NOTE: TBD NYI */
5846
5847/* png_set_add_alpha, (png_structp png_ptr, png_uint_32 filler, int flags)); */
5848/*NOTE: TBD NYI */
5849
5850/* png_set_packing */
5851/*NOTE: TBD NYI */
5852
5853/* png_set_packswap */
5854/*NOTE: TBD NYI */
5855
5856/* png_set_invert_mono */
5857/*NOTE: TBD NYI */
5858
5859/* png_set_shift(png_structp, png_const_color_8p true_bits) */
5860/*NOTE: TBD NYI */
5861
John Bowler9994f252011-05-15 18:52:39 -05005862static void
5863perform_transform_test(png_modifier *pm)
John Bowlerf21a0d02011-01-23 23:55:19 -06005864{
John Bowler9994f252011-05-15 18:52:39 -05005865 png_byte colour_type = 0;
5866 png_byte bit_depth = 0;
5867 int palette_number = 0;
5868
5869 while (next_format(&colour_type, &bit_depth, &palette_number))
John Bowlerf21a0d02011-01-23 23:55:19 -06005870 {
John Bowlerafea7d12011-01-28 06:38:14 -06005871 png_uint_32 counter = 0;
John Bowlerf21a0d02011-01-23 23:55:19 -06005872 size_t base_pos;
5873 char name[64];
5874
5875 base_pos = safecat(name, sizeof name, 0, "transform:");
5876
5877 for (;;)
5878 {
5879 size_t pos = base_pos;
5880 PNG_CONST image_transform *list = 0;
5881
Glenn Randers-Pehrson73904f52011-05-15 19:38:06 -05005882 /* 'max' is currently hardwired to '1'; this should be settable on the
John Bowler9994f252011-05-15 18:52:39 -05005883 * command line.
5884 */
5885 counter = image_transform_add(&list, 1/*max*/, counter,
5886 name, sizeof name, &pos, colour_type, bit_depth);
John Bowlerf21a0d02011-01-23 23:55:19 -06005887
5888 if (counter == 0)
5889 break;
5890
5891 /* The command line can change this to checking interlaced images. */
John Bowler9994f252011-05-15 18:52:39 -05005892 transform_test(pm, FILEID(colour_type, bit_depth, palette_number,
5893 pm->interlace_type, 0, 0, 0), list, name);
John Bowlerf21a0d02011-01-23 23:55:19 -06005894
5895 if (fail(pm))
John Bowler9994f252011-05-15 18:52:39 -05005896 return;
John Bowlerf21a0d02011-01-23 23:55:19 -06005897 }
5898 }
John Bowlerf21a0d02011-01-23 23:55:19 -06005899}
John Bowler4a12f4a2011-04-17 18:34:22 -05005900#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06005901
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005902/********************************* GAMMA TESTS ********************************/
John Bowler4a12f4a2011-04-17 18:34:22 -05005903#ifdef PNG_READ_GAMMA_SUPPORTED
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005904/* Gamma test images. */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005905typedef struct gamma_modification
5906{
5907 png_modification this;
5908 png_fixed_point gamma;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005909} gamma_modification;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005910
5911static int
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005912gamma_modify(png_modifier *pm, png_modification *me, int add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005913{
John Bowlerafea7d12011-01-28 06:38:14 -06005914 UNUSED(add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005915 /* This simply dumps the given gamma value into the buffer. */
5916 png_save_uint_32(pm->buffer, 4);
5917 png_save_uint_32(pm->buffer+4, CHUNK_gAMA);
5918 png_save_uint_32(pm->buffer+8, ((gamma_modification*)me)->gamma);
5919 return 1;
5920}
5921
5922static void
John Bowler168a4332011-01-16 19:32:22 -06005923gamma_modification_init(gamma_modification *me, png_modifier *pm, double gammad)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005924{
Glenn Randers-Pehrson21b4b332010-08-18 07:12:38 -05005925 double g;
5926
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005927 modification_init(&me->this);
5928 me->this.chunk = CHUNK_gAMA;
5929 me->this.modify_fn = gamma_modify;
5930 me->this.add = CHUNK_PLTE;
John Bowler168a4332011-01-16 19:32:22 -06005931 g = floor(gammad * 100000 + .5);
Glenn Randers-Pehrson21b4b332010-08-18 07:12:38 -05005932 me->gamma = (png_fixed_point)g;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005933 me->this.next = pm->modifications;
5934 pm->modifications = &me->this;
5935}
5936
5937typedef struct srgb_modification
5938{
5939 png_modification this;
5940 png_byte intent;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005941} srgb_modification;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005942
5943static int
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005944srgb_modify(png_modifier *pm, png_modification *me, int add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005945{
John Bowlerafea7d12011-01-28 06:38:14 -06005946 UNUSED(add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005947 /* As above, ignore add and just make a new chunk */
5948 png_save_uint_32(pm->buffer, 1);
5949 png_save_uint_32(pm->buffer+4, CHUNK_sRGB);
5950 pm->buffer[8] = ((srgb_modification*)me)->intent;
5951 return 1;
5952}
5953
5954static void
5955srgb_modification_init(srgb_modification *me, png_modifier *pm, png_byte intent)
5956{
5957 modification_init(&me->this);
5958 me->this.chunk = CHUNK_sBIT;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005959
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005960 if (intent <= 3) /* if valid, else *delete* sRGB chunks */
5961 {
5962 me->this.modify_fn = srgb_modify;
5963 me->this.add = CHUNK_PLTE;
5964 me->intent = intent;
5965 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005966
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005967 else
5968 {
5969 me->this.modify_fn = 0;
5970 me->this.add = 0;
5971 me->intent = 0;
5972 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005973
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005974 me->this.next = pm->modifications;
5975 pm->modifications = &me->this;
5976}
5977
5978typedef struct sbit_modification
5979{
5980 png_modification this;
5981 png_byte sbit;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005982} sbit_modification;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005983
5984static int
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05005985sbit_modify(png_modifier *pm, png_modification *me, int add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05005986{
5987 png_byte sbit = ((sbit_modification*)me)->sbit;
5988 if (pm->bit_depth > sbit)
5989 {
5990 int cb = 0;
5991 switch (pm->colour_type)
5992 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05005993 case 0:
5994 cb = 1;
5995 break;
5996
5997 case 2:
5998 case 3:
5999 cb = 3;
6000 break;
6001
6002 case 4:
6003 cb = 2;
6004 break;
6005
6006 case 6:
6007 cb = 4;
6008 break;
6009
6010 default:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006011 png_error(pm->this.pread,
6012 "unexpected colour type in sBIT modification");
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006013 }
6014
6015 png_save_uint_32(pm->buffer, cb);
6016 png_save_uint_32(pm->buffer+4, CHUNK_sBIT);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05006017
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006018 while (cb > 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05006019 (pm->buffer+8)[--cb] = sbit;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05006020
6021 return 1;
6022 }
6023 else if (!add)
6024 {
6025 /* Remove the sBIT chunk */
6026 pm->buffer_count = pm->buffer_position = 0;
6027 return 1;
6028 }
6029 else
6030 return 0; /* do nothing */
6031}
6032
6033static void
6034sbit_modification_init(sbit_modification *me, png_modifier *pm, png_byte sbit)
6035{
6036 modification_init(&me->this);
6037 me->this.chunk = CHUNK_sBIT;
6038 me->this.modify_fn = sbit_modify;
6039 me->this.add = CHUNK_PLTE;
6040 me->sbit = sbit;
6041 me->this.next = pm->modifications;
6042 pm->modifications = &me->this;
6043}
6044
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006045/* Reader callbacks and implementations, where they differ from the standard
6046 * ones.
6047 */
6048typedef struct gamma_display
6049{
6050 standard_display this;
6051
6052 /* Parameters */
6053 png_modifier* pm;
6054 double file_gamma;
6055 double screen_gamma;
John Bowlerd273ad22011-05-07 21:00:28 -05006056 double background_gamma;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006057 png_byte sbit;
6058 int threshold_test;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006059 int use_input_precision;
6060 int strip16;
John Bowlerd273ad22011-05-07 21:00:28 -05006061 int expand16;
6062 int do_background;
6063 png_color_16 background_color;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006064
6065 /* Local variables */
6066 double maxerrout;
6067 double maxerrpc;
6068 double maxerrabs;
6069} gamma_display;
6070
John Bowlerd273ad22011-05-07 21:00:28 -05006071#define ALPHA_MODE_OFFSET 4
6072
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006073static void
John Bowler660c6e42010-12-19 06:22:23 -06006074gamma_display_init(gamma_display *dp, png_modifier *pm, png_uint_32 id,
6075 double file_gamma, double screen_gamma, png_byte sbit, int threshold_test,
John Bowler9994f252011-05-15 18:52:39 -05006076 int use_input_precision, int strip16, int expand16,
John Bowlerd273ad22011-05-07 21:00:28 -05006077 int do_background, PNG_CONST png_color_16 *pointer_to_the_background_color,
6078 double background_gamma)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006079{
6080 /* Standard fields */
John Bowler660c6e42010-12-19 06:22:23 -06006081 standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06006082
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006083 /* Parameter fields */
6084 dp->pm = pm;
6085 dp->file_gamma = file_gamma;
6086 dp->screen_gamma = screen_gamma;
John Bowlerd273ad22011-05-07 21:00:28 -05006087 dp->background_gamma = background_gamma;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006088 dp->sbit = sbit;
6089 dp->threshold_test = threshold_test;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006090 dp->use_input_precision = use_input_precision;
6091 dp->strip16 = strip16;
John Bowlerd273ad22011-05-07 21:00:28 -05006092 dp->expand16 = expand16;
6093 dp->do_background = do_background;
6094 if (do_background && pointer_to_the_background_color != 0)
6095 dp->background_color = *pointer_to_the_background_color;
6096 else
6097 memset(&dp->background_color, 0, sizeof dp->background_color);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006098
6099 /* Local variable fields */
6100 dp->maxerrout = dp->maxerrpc = dp->maxerrabs = 0;
6101}
6102
6103static void
6104gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi)
6105{
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05006106 /* Reuse the standard stuff as appropriate. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006107 standard_info_part1(&dp->this, pp, pi);
6108
6109 /* If requested strip 16 to 8 bits - this is handled automagically below
6110 * because the output bit depth is read from the library. Note that there
6111 * are interactions with sBIT but, internally, libpng makes sbit at most
6112 * PNG_MAX_GAMMA_8 when doing the following.
6113 */
6114 if (dp->strip16)
John Bowlerb54498e2010-12-08 16:26:21 -06006115# ifdef PNG_READ_16_TO_8_SUPPORTED
Glenn Randers-Pehrsonfded04f2010-08-27 14:21:21 -05006116 png_set_strip_16(pp);
6117# else
6118 png_error(pp, "strip16 (16 to 8 bit conversion) not supported");
6119# endif
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006120
John Bowlerd273ad22011-05-07 21:00:28 -05006121 if (dp->expand16)
6122# ifdef PNG_READ_EXPAND_16_SUPPORTED
6123 png_set_expand_16(pp);
6124# else
6125 png_error(pp, "expand16 (8 to 16 bit conversion) not supported");
6126# endif
6127
6128 if (dp->do_background >= ALPHA_MODE_OFFSET)
6129 {
6130# ifdef PNG_READ_ALPHA_MODE_SUPPORTED
6131 {
6132 /* This tests the alpha mode handling, if supported. */
6133 int mode = dp->do_background - ALPHA_MODE_OFFSET;
6134
6135 /* The gamma value is the output gamma, and is in the standard,
6136 * non-inverted, represenation. It provides a default for the PNG file
6137 * gamma, but since the file has a gAMA chunk this does not matter.
6138 */
6139 PNG_CONST double sg = dp->screen_gamma;
6140# ifndef PNG_FLOATING_POINT_SUPPORTED
6141 PNG_CONST png_fixed_point g = (png_fixed_point)(sg*100000+.5);
6142# endif
6143
6144# ifdef PNG_FLOATING_POINT_SUPPORTED
6145 png_set_alpha_mode(pp, mode, sg);
6146# else
6147 png_set_alpha_mode_fixed(pp, mode, g);
6148# endif
6149
6150 /* However, for the standard Porter-Duff algorithm the output defaults
6151 * to be linear, so if the test requires non-linear output it must be
6152 * corrected here.
6153 */
6154 if (mode == PNG_ALPHA_STANDARD && sg != 1)
6155 {
6156# ifdef PNG_FLOATING_POINT_SUPPORTED
6157 png_set_gamma(pp, sg, dp->file_gamma);
6158# else
6159 png_fixed_point f = (png_fixed_point)(dp->file_gamma*100000+.5);
6160 png_set_gamma_fixed(pp, g, f);
6161# endif
6162 }
6163 }
6164# else
6165 png_error(pp, "alpha mode handling not supported");
6166# endif
6167 }
6168
6169 else
6170 {
6171 /* Set up gamma processing. */
6172# ifdef PNG_FLOATING_POINT_SUPPORTED
6173 png_set_gamma(pp, dp->screen_gamma, dp->file_gamma);
6174# else
6175 {
6176 png_fixed_point s = (png_fixed_point)(dp->screen_gamma*100000+.5);
6177 png_fixed_point f = (png_fixed_point)(dp->file_gamma*100000+.5);
6178 png_set_gamma_fixed(pp, s, f);
6179 }
6180# endif
6181
6182 if (dp->do_background)
6183 {
6184# ifdef PNG_READ_BACKGROUND_SUPPORTED
6185 /* NOTE: this assumes the caller provided the correct background gamma!
6186 */
6187 PNG_CONST double bg = dp->background_gamma;
6188# ifndef PNG_FLOATING_POINT_SUPPORTED
6189 PNG_CONST png_fixed_point g = (png_fixed_point)(bg*100000+.5);
6190# endif
6191
6192# ifdef PNG_FLOATING_POINT_SUPPORTED
6193 png_set_background(pp, &dp->background_color, dp->do_background,
6194 0/*need_expand*/, bg);
6195# else
6196 png_set_background_fixed(pp, &dp->background_color,
6197 dp->do_background, 0/*need_expand*/, g);
6198# endif
6199# else
6200 png_error(pp, "png_set_background not supported");
6201# endif
6202 }
6203 }
6204
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006205 png_read_update_info(pp, pi);
6206
6207 /* Now we may get a different cbRow: */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006208 standard_info_part2(&dp->this, pp, pi, 1 /*images*/);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006209}
6210
6211static void
6212gamma_info(png_structp pp, png_infop pi)
6213{
6214 gamma_info_imp(png_get_progressive_ptr(pp), pp, pi);
6215}
6216
John Bowlerd273ad22011-05-07 21:00:28 -05006217/* Validate a single component value - the routine gets the input and output
6218 * sample values as unscaled PNG component values along with a cache of all the
6219 * information required to validate the values.
6220 */
6221typedef struct validate_info
6222{
6223 png_structp pp;
6224 gamma_display *dp;
6225 png_byte sbit;
6226 int use_input_precision;
6227 int do_background;
6228 int strip16;
6229 unsigned int sbit_max;
6230 unsigned int isbit_shift;
6231 unsigned int outmax;
6232
6233 double gamma_correction; /* Overall correction required. */
6234 double file_inverse; /* Inverse of file gamma. */
6235 double screen_gamma;
6236 double screen_inverse; /* Inverse of screen gamma. */
6237
6238 double background_red; /* Linear background value, red or gray. */
6239 double background_green;
6240 double background_blue;
6241
6242 double maxabs;
John Bowler5441e182011-05-18 18:57:12 -05006243 double maxpc;
John Bowlerd273ad22011-05-07 21:00:28 -05006244 double maxcalc;
6245 double maxout;
John Bowler5441e182011-05-18 18:57:12 -05006246 double maxout_total; /* Total including quantization error */
John Bowler6f55ee22011-06-11 07:28:06 -05006247 double outlog;
John Bowler5441e182011-05-18 18:57:12 -05006248 int outquant;
John Bowlerd273ad22011-05-07 21:00:28 -05006249}
6250validate_info;
6251
6252static void
6253init_validate_info(validate_info *vi, gamma_display *dp, png_struct *pp,
John Bowler5441e182011-05-18 18:57:12 -05006254 int in_depth, int out_depth)
John Bowlerd273ad22011-05-07 21:00:28 -05006255{
John Bowler5441e182011-05-18 18:57:12 -05006256 PNG_CONST unsigned int outmax = (1U<<out_depth)-1;
John Bowlerd273ad22011-05-07 21:00:28 -05006257
6258 vi->pp = pp;
6259 vi->dp = dp;
John Bowler5441e182011-05-18 18:57:12 -05006260
6261 if (dp->sbit > 0 && dp->sbit < in_depth)
6262 {
6263 vi->sbit = dp->sbit;
6264 vi->isbit_shift = in_depth - dp->sbit;
6265 }
6266
6267 else
6268 {
6269 vi->sbit = (png_byte)in_depth;
6270 vi->isbit_shift = 0;
6271 }
6272
6273 vi->sbit_max = (1U << vi->sbit)-1;
John Bowlerd273ad22011-05-07 21:00:28 -05006274
6275 /* This mimics the libpng threshold test, '0' is used to prevent gamma
6276 * correction in the validation test.
6277 */
6278 vi->screen_gamma = dp->screen_gamma;
6279 if (fabs(vi->screen_gamma-1) < PNG_GAMMA_THRESHOLD)
6280 vi->screen_gamma = vi->screen_inverse = 0;
6281 else
6282 vi->screen_inverse = 1/vi->screen_gamma;
6283
6284 vi->use_input_precision = dp->use_input_precision;
6285 vi->outmax = outmax;
John Bowler5441e182011-05-18 18:57:12 -05006286 vi->maxabs = abserr(dp->pm, in_depth, out_depth);
6287 vi->maxpc = pcerr(dp->pm, in_depth, out_depth);
6288 vi->maxcalc = calcerr(dp->pm, in_depth, out_depth);
6289 vi->maxout = outerr(dp->pm, in_depth, out_depth);
6290 vi->outquant = output_quantization_factor(dp->pm, in_depth, out_depth);
6291 vi->maxout_total = vi->maxout + vi->outquant * .5;
John Bowler6f55ee22011-06-11 07:28:06 -05006292 vi->outlog = outlog(dp->pm, in_depth, out_depth);
John Bowlerd273ad22011-05-07 21:00:28 -05006293
John Bowler5441e182011-05-18 18:57:12 -05006294 if ((dp->this.colour_type & PNG_COLOR_MASK_ALPHA) != 0 ||
6295 (dp->this.colour_type == 3 && dp->this.is_transparent))
John Bowlerd273ad22011-05-07 21:00:28 -05006296 {
6297 vi->do_background = dp->do_background;
6298
6299 if (vi->do_background != 0)
6300 {
6301 PNG_CONST double bg_inverse = 1/dp->background_gamma;
6302 double r, g, b;
6303
6304 /* Caller must at least put the gray value into the red channel */
6305 r = dp->background_color.red; r /= outmax;
6306 g = dp->background_color.green; g /= outmax;
6307 b = dp->background_color.blue; b /= outmax;
6308
6309# if 0
6310 /* libpng doesn't do this optimization, if we do pngvalid will fail.
6311 */
6312 if (fabs(bg_inverse-1) >= PNG_GAMMA_THRESHOLD)
6313# endif
6314 {
6315 r = pow(r, bg_inverse);
6316 g = pow(g, bg_inverse);
6317 b = pow(b, bg_inverse);
6318 }
6319
6320 vi->background_red = r;
6321 vi->background_green = g;
6322 vi->background_blue = b;
6323 }
6324 }
6325 else
6326 vi->do_background = 0;
6327
6328 if (vi->do_background == 0)
6329 vi->background_red = vi->background_green = vi->background_blue = 0;
6330
6331 vi->gamma_correction = 1/(dp->file_gamma*dp->screen_gamma);
6332 if (fabs(vi->gamma_correction-1) < PNG_GAMMA_THRESHOLD)
6333 vi->gamma_correction = 0;
6334
6335 vi->file_inverse = 1/dp->file_gamma;
6336 if (fabs(vi->file_inverse-1) < PNG_GAMMA_THRESHOLD)
6337 vi->file_inverse = 0;
6338
6339 vi->strip16 = dp->strip16;
6340}
6341
John Bowler5441e182011-05-18 18:57:12 -05006342/* This function handles composition of a single non-alpha component. The
6343 * argument is the input sample value, in the range 0..1, and the alpha value.
6344 * The result is the composed, linear, input sample. If alpha is less than zero
6345 * this is the alpha component and the function should not be called!
6346 */
6347static double
6348gamma_component_compose(int do_background, double input_sample, double alpha,
6349 double background, int *compose)
6350{
6351 switch (do_background)
6352 {
6353 case PNG_BACKGROUND_GAMMA_SCREEN:
6354 case PNG_BACKGROUND_GAMMA_FILE:
6355 case PNG_BACKGROUND_GAMMA_UNIQUE:
6356 /* Standard PNG background processing. */
6357 if (alpha < 1)
6358 {
6359 if (alpha > 0)
6360 {
6361 input_sample = input_sample * alpha + background * (1-alpha);
6362 if (compose != NULL)
6363 *compose = 1;
6364 }
6365
6366 else
6367 input_sample = background;
6368 }
6369 break;
6370
6371#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
6372 case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
6373 case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
6374 /* The components are premultiplied in either case and the output is
6375 * gamma encoded (to get standard Porter-Duff we expect the output
6376 * gamma to be set to 1.0!)
6377 */
6378 case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
6379 /* The optimization is that the partial-alpha entries are linear
6380 * while the opaque pixels are gamma encoded, but this only affects the
6381 * output encoding.
6382 */
6383 if (alpha < 1)
6384 {
6385 if (alpha > 0)
6386 {
6387 input_sample *= alpha;
6388 if (compose != NULL)
6389 *compose = 1;
6390 }
6391
6392 else
6393 input_sample = 0;
6394 }
6395 break;
6396#endif
6397
6398 default:
6399 /* Standard cases where no compositing is done (so the component
6400 * value is already correct.)
6401 */
6402 break;
6403 }
6404
6405 return input_sample;
6406}
6407
John Bowlerd273ad22011-05-07 21:00:28 -05006408/* This API returns the encoded *input* component, in the range 0..1 */
6409static double
6410gamma_component_validate(PNG_CONST char *name, PNG_CONST validate_info *vi,
6411 PNG_CONST unsigned int id, PNG_CONST unsigned int od,
6412 PNG_CONST double alpha /* <0 for the alpha channel itself */,
6413 PNG_CONST double background /* component background value */)
6414{
6415 PNG_CONST unsigned int isbit = id >> vi->isbit_shift;
6416 PNG_CONST unsigned int sbit_max = vi->sbit_max;
6417 PNG_CONST unsigned int outmax = vi->outmax;
6418 PNG_CONST int do_background = vi->do_background;
6419
6420 double i;
6421
6422 /* First check on the 'perfect' result obtained from the digitized input
6423 * value, id, and compare this against the actual digitized result, 'od'.
6424 * 'i' is the input result in the range 0..1:
John Bowlerd273ad22011-05-07 21:00:28 -05006425 */
6426 i = isbit; i /= sbit_max;
6427
6428 /* Check for the fast route: if we don't do any background composition or if
6429 * this is the alpha channel ('alpha' < 0) or if the pixel is opaque then
6430 * just use the gamma_correction field to correct to the final output gamma.
6431 */
6432 if (alpha == 1 /* opaque pixel component */ || !do_background
6433#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
6434 || do_background == ALPHA_MODE_OFFSET + PNG_ALPHA_PNG
6435#endif
6436 || (alpha < 0 /* alpha channel */
6437#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
6438 && do_background != ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN
6439#endif
6440 ))
6441 {
6442 /* Then get the gamma corrected version of 'i' and compare to 'od', any
6443 * error less than .5 is insignificant - just quantization of the output
6444 * value to the nearest digital value (nevertheless the error is still
6445 * recorded - it's interesting ;-)
6446 */
6447 double encoded_sample = i;
6448 double encoded_error;
6449
6450 /* alpha less than 0 indicates the alpha channel, which is always linear
6451 */
6452 if (alpha >= 0 && vi->gamma_correction > 0)
6453 encoded_sample = pow(encoded_sample, vi->gamma_correction);
6454 encoded_sample *= outmax;
6455
6456 encoded_error = fabs(od-encoded_sample);
6457
6458 if (encoded_error > vi->dp->maxerrout)
6459 vi->dp->maxerrout = encoded_error;
6460
John Bowler6f55ee22011-06-11 07:28:06 -05006461 if (encoded_error < vi->maxout_total && encoded_error < vi->outlog)
John Bowlerd273ad22011-05-07 21:00:28 -05006462 return i;
6463 }
6464
6465 /* The slow route - attempt to do linear calculations. */
6466 /* There may be an error, or background processing is required, so calculate
6467 * the actual sample values - unencoded light intensity values. Note that in
6468 * practice these are not completely unencoded because they include a
6469 * 'viewing correction' to decrease or (normally) increase the perceptual
6470 * contrast of the image. There's nothing we can do about this - we don't
6471 * know what it is - so assume the unencoded value is perceptually linear.
6472 */
6473 {
6474 double input_sample = i; /* In range 0..1 */
John Bowler6f55ee22011-06-11 07:28:06 -05006475 double output, error, encoded_sample, encoded_error;
John Bowlerd273ad22011-05-07 21:00:28 -05006476 double es_lo, es_hi;
John Bowler5441e182011-05-18 18:57:12 -05006477 int compose = 0; /* Set to one if composition done */
6478 int output_is_encoded; /* Set if encoded to screen gamma */
John Bowlerd273ad22011-05-07 21:00:28 -05006479 int log_max_error = 1; /* Check maximum error values */
John Bowler6f55ee22011-06-11 07:28:06 -05006480 png_const_charp pass = 0; /* Reason test passes (or 0 for fail) */
John Bowlerd273ad22011-05-07 21:00:28 -05006481
6482 /* Convert to linear light (with the above caveat.) The alpha channel is
6483 * already linear.
6484 */
John Bowler5441e182011-05-18 18:57:12 -05006485 if (alpha >= 0)
6486 {
6487 int tcompose;
6488
John Bowler6f55ee22011-06-11 07:28:06 -05006489 if (vi->file_inverse > 0)
John Bowler5441e182011-05-18 18:57:12 -05006490 input_sample = pow(input_sample, vi->file_inverse);
6491
6492 /* Handle the compose processing: */
6493 tcompose = 0;
6494 input_sample = gamma_component_compose(do_background, input_sample,
6495 alpha, background, &tcompose);
6496
6497 if (tcompose)
6498 compose = 1;
6499 }
John Bowlerd273ad22011-05-07 21:00:28 -05006500
6501 /* And similarly for the output value, but we need to check the background
John Bowler5441e182011-05-18 18:57:12 -05006502 * handling to linearize it correctly.
John Bowlerd273ad22011-05-07 21:00:28 -05006503 */
6504 output = od;
6505 output /= outmax;
6506
John Bowler5441e182011-05-18 18:57:12 -05006507 output_is_encoded = vi->screen_gamma > 0;
John Bowlerd273ad22011-05-07 21:00:28 -05006508
John Bowler5441e182011-05-18 18:57:12 -05006509 if (alpha < 0) /* The alpha channel */
6510 {
John Bowler59a6c372011-06-11 06:51:06 -05006511#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
John Bowler5441e182011-05-18 18:57:12 -05006512 if (do_background != ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN)
John Bowler59a6c372011-06-11 06:51:06 -05006513#endif
John Bowler5441e182011-05-18 18:57:12 -05006514 {
John Bowlerd273ad22011-05-07 21:00:28 -05006515 /* In all other cases the output alpha channel is linear already,
6516 * don't log errors here, they are much larger in linear data.
6517 */
John Bowler5441e182011-05-18 18:57:12 -05006518 output_is_encoded = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05006519 log_max_error = 0;
John Bowler5441e182011-05-18 18:57:12 -05006520 }
John Bowlerd273ad22011-05-07 21:00:28 -05006521 }
6522
John Bowler59a6c372011-06-11 06:51:06 -05006523#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
John Bowler5441e182011-05-18 18:57:12 -05006524 else /* A component */
John Bowlerd273ad22011-05-07 21:00:28 -05006525 {
John Bowler5441e182011-05-18 18:57:12 -05006526 if (do_background == ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED &&
6527 alpha < 1) /* the optimized case - linear output */
6528 {
6529 if (alpha > 0) log_max_error = 0;
6530 output_is_encoded = 0;
6531 }
John Bowlerd273ad22011-05-07 21:00:28 -05006532 }
John Bowler59a6c372011-06-11 06:51:06 -05006533#endif
John Bowlerd273ad22011-05-07 21:00:28 -05006534
John Bowler5441e182011-05-18 18:57:12 -05006535 if (output_is_encoded)
6536 output = pow(output, vi->screen_gamma);
6537
John Bowlerd273ad22011-05-07 21:00:28 -05006538 /* Calculate (or recalculate) the encoded_sample value and repeat the
6539 * check above (unnecessary if we took the fast route, but harmless.)
6540 */
6541 encoded_sample = input_sample;
6542 if (output_is_encoded)
6543 encoded_sample = pow(encoded_sample, vi->screen_inverse);
6544 encoded_sample *= outmax;
6545
John Bowler6f55ee22011-06-11 07:28:06 -05006546 encoded_error = fabs(od-encoded_sample);
6547
6548 /* Don't log errors in the alpha channel, or the 'optimized' case,
6549 * neither are significant to the overall perception.
6550 */
6551 if (log_max_error && encoded_error > vi->dp->maxerrout)
6552 vi->dp->maxerrout = encoded_error;
6553
6554 if (encoded_error < vi->maxout_total)
John Bowlerd273ad22011-05-07 21:00:28 -05006555 {
John Bowler6f55ee22011-06-11 07:28:06 -05006556 if (encoded_error < vi->outlog)
John Bowlerd273ad22011-05-07 21:00:28 -05006557 return i;
John Bowler6f55ee22011-06-11 07:28:06 -05006558
6559 /* Test passed but error is bigger than the log limit, record why the
6560 * test passed:
6561 */
6562 pass = "less than maxout:\n";
John Bowlerd273ad22011-05-07 21:00:28 -05006563 }
6564
John Bowler5441e182011-05-18 18:57:12 -05006565 /* i: the original input value in the range 0..1
6566 *
6567 * pngvalid calculations:
6568 * input_sample: linear result; i linearized and composed, range 0..1
6569 * encoded_sample: encoded result; input_sample scaled to ouput bit depth
6570 *
6571 * libpng calculations:
6572 * output: linear result; od scaled to 0..1 and linearized
6573 * od: encoded result from libpng
6574 */
6575
John Bowlerd273ad22011-05-07 21:00:28 -05006576 /* Now we have the numbers for real errors, both absolute values as as a
6577 * percentage of the correct value (output):
6578 */
6579 error = fabs(input_sample-output);
6580
6581 if (log_max_error && error > vi->dp->maxerrabs)
6582 vi->dp->maxerrabs = error;
6583
6584 /* The following is an attempt to ignore the tendency of quantization to
John Bowler5441e182011-05-18 18:57:12 -05006585 * dominate the percentage errors for lower result values:
John Bowlerd273ad22011-05-07 21:00:28 -05006586 */
John Bowler5441e182011-05-18 18:57:12 -05006587 if (log_max_error && input_sample > .5)
John Bowlerd273ad22011-05-07 21:00:28 -05006588 {
6589 double percentage_error = error/input_sample;
6590 if (percentage_error > vi->dp->maxerrpc)
6591 vi->dp->maxerrpc = percentage_error;
6592 }
6593
6594 /* Now calculate the digitization limits for 'encoded_sample' using the
6595 * 'max' values. Note that maxout is in the encoded space but maxpc and
6596 * maxabs are in linear light space.
6597 *
6598 * First find the maximum error in linear light space, range 0..1:
6599 */
6600 {
6601 double tmp = input_sample * vi->maxpc;
6602 if (tmp < vi->maxabs) tmp = vi->maxabs;
John Bowler5441e182011-05-18 18:57:12 -05006603 /* If 'compose' is true the composition was done in linear space using
6604 * integer arithmetic. This introduces an extra error of +/- 0.5 (at
6605 * least) in the integer space used. 'maxcalc' records this, taking
6606 * into account the possibility that even for 16 bit output 8 bit space
6607 * may have been used.
6608 */
John Bowlerd273ad22011-05-07 21:00:28 -05006609 if (compose && tmp < vi->maxcalc) tmp = vi->maxcalc;
6610
John Bowler5441e182011-05-18 18:57:12 -05006611 /* The 'maxout' value refers to the encoded result, to compare with
6612 * this encode input_sample adjusted by the maximum error (tmp) above.
6613 */
John Bowlerd273ad22011-05-07 21:00:28 -05006614 es_lo = encoded_sample - vi->maxout;
6615
6616 if (es_lo > 0 && input_sample-tmp > 0)
6617 {
6618 double low_value = input_sample-tmp;
6619 if (output_is_encoded)
6620 low_value = pow(low_value, vi->screen_inverse);
6621 low_value *= outmax;
6622 if (low_value < es_lo) es_lo = low_value;
John Bowler5441e182011-05-18 18:57:12 -05006623
6624 /* Quantize this appropriately: */
6625 es_lo = ceil(es_lo / vi->outquant - .5) * vi->outquant;
John Bowlerd273ad22011-05-07 21:00:28 -05006626 }
6627
6628 else
6629 es_lo = 0;
6630
6631 es_hi = encoded_sample + vi->maxout;
6632
6633 if (es_hi < outmax && input_sample+tmp < 1)
6634 {
6635 double high_value = input_sample+tmp;
6636 if (output_is_encoded)
6637 high_value = pow(high_value, vi->screen_inverse);
6638 high_value *= outmax;
6639 if (high_value > es_hi) es_hi = high_value;
John Bowler5441e182011-05-18 18:57:12 -05006640
6641 es_hi = floor(es_hi / vi->outquant + .5) * vi->outquant;
John Bowlerd273ad22011-05-07 21:00:28 -05006642 }
6643
6644 else
6645 es_hi = outmax;
6646 }
6647
6648 /* The primary test is that the final encoded value returned by the
6649 * library should be between the two limits (inclusive) that were
John Bowler5441e182011-05-18 18:57:12 -05006650 * calculated above.
John Bowlerd273ad22011-05-07 21:00:28 -05006651 */
John Bowler6f55ee22011-06-11 07:28:06 -05006652 if (od >= es_lo && od <= es_hi)
John Bowlerd273ad22011-05-07 21:00:28 -05006653 {
John Bowler6f55ee22011-06-11 07:28:06 -05006654 /* The value passes, but we may need to log the information anyway. */
6655 if (encoded_error < vi->outlog)
6656 return i;
6657
6658 if (pass == 0)
6659 pass = "within digitization limits:\n";
6660 }
6661
6662 {
6663 /* There has been an error in processing, or we need to log this
6664 * value.
6665 */
John Bowlerd273ad22011-05-07 21:00:28 -05006666 double is_lo, is_hi;
6667
John Bowler6f55ee22011-06-11 07:28:06 -05006668 /* pass is set at this point if either of the tests above would have
6669 * passed. Don't do these additional tests here - just log the
6670 * original [es_lo..es_hi] values.
6671 */
6672 if (pass == 0 && vi->use_input_precision)
John Bowlerd273ad22011-05-07 21:00:28 -05006673 {
6674 /* Ok, something is wrong - this actually happens in current libpng
John Bowler5441e182011-05-18 18:57:12 -05006675 * 16-to-8 processing. Assume that the input value (id, adjusted
6676 * for sbit) can be anywhere between value-.5 and value+.5 - quite a
John Bowlerd273ad22011-05-07 21:00:28 -05006677 * large range if sbit is low.
6678 */
6679 double tmp = (isbit - .5)/sbit_max;
6680
John Bowler5441e182011-05-18 18:57:12 -05006681 if (tmp <= 0)
6682 tmp = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05006683
John Bowler5441e182011-05-18 18:57:12 -05006684 else if (alpha >= 0 && vi->file_inverse > 0 && tmp < 1)
6685 tmp = pow(tmp, vi->file_inverse);
6686
6687 tmp = gamma_component_compose(do_background, tmp, alpha, background,
6688 NULL);
6689
6690 if (output_is_encoded && tmp > 0 && tmp < 1)
6691 tmp = pow(tmp, vi->screen_inverse);
6692
6693 is_lo = ceil(outmax * tmp - vi->maxout_total);
6694
6695 if (is_lo < 0)
John Bowlerd273ad22011-05-07 21:00:28 -05006696 is_lo = 0;
6697
6698 tmp = (isbit + .5)/sbit_max;
6699
John Bowler5441e182011-05-18 18:57:12 -05006700 if (tmp <= 0)
6701 tmp = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05006702
John Bowler5441e182011-05-18 18:57:12 -05006703 else if (alpha >= 0 && vi->file_inverse > 0 && tmp < 1)
6704 tmp = pow(tmp, vi->file_inverse);
6705
6706 tmp = gamma_component_compose(do_background, tmp, alpha, background,
6707 NULL);
6708
6709 if (output_is_encoded && tmp > 0 && tmp < 1)
6710 tmp = pow(tmp, vi->screen_inverse);
6711
6712 is_hi = floor(outmax * tmp + vi->maxout_total);
6713
6714 if (is_hi > outmax)
John Bowlerd273ad22011-05-07 21:00:28 -05006715 is_hi = outmax;
6716
John Bowler5441e182011-05-18 18:57:12 -05006717 if (!(od < is_lo || od > is_hi))
John Bowler6f55ee22011-06-11 07:28:06 -05006718 {
6719 if (encoded_error < vi->outlog)
6720 return i;
6721
6722 pass = "within input precision limits:\n";
6723 }
John Bowlerd273ad22011-05-07 21:00:28 -05006724
6725 /* One last chance. If this is an alpha channel and the 16to8
6726 * option has been used and 'inaccurate' scaling is used then the
6727 * bit reduction is obtained by simply using the top 8 bits of the
6728 * value.
6729 */
6730# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
6731 /* This may be required for other components in the future, but
6732 * at present the presence of gamma correction effectively
6733 * prevents the errors in the component scaling (I don't quite
6734 * understand why, but since it's better this way I care not to
6735 * ask, JB 20110419.)
6736 */
John Bowler6f55ee22011-06-11 07:28:06 -05006737 if (pass == 0 && alpha < 0 && vi->strip16 && vi->sbit > 8 &&
John Bowlerd273ad22011-05-07 21:00:28 -05006738 vi->sbit + vi->isbit_shift == 16)
6739 {
6740 tmp = ((id >> 8) - .5)/255;
6741
6742 if (tmp > 0)
6743 {
John Bowler5441e182011-05-18 18:57:12 -05006744 is_lo = ceil(outmax * tmp - vi->maxout_total);
John Bowlerd273ad22011-05-07 21:00:28 -05006745 if (is_lo < 0) is_lo = 0;
6746 }
6747
6748 else
6749 is_lo = 0;
6750
6751 tmp = ((id >> 8) + .5)/255;
6752
6753 if (tmp < 1)
6754 {
John Bowler5441e182011-05-18 18:57:12 -05006755 is_hi = floor(outmax * tmp + vi->maxout_total);
John Bowlerd273ad22011-05-07 21:00:28 -05006756 if (is_hi > outmax) is_hi = outmax;
6757 }
6758
6759 else
6760 is_hi = outmax;
6761
John Bowler5441e182011-05-18 18:57:12 -05006762 if (!(od < is_lo || od > is_hi))
John Bowler6f55ee22011-06-11 07:28:06 -05006763 {
6764 if (encoded_error < vi->outlog)
6765 return i;
6766
6767 pass = "within 8 bit limits:\n";
6768 }
John Bowlerd273ad22011-05-07 21:00:28 -05006769 }
6770# endif
6771 }
6772 else /* !use_input_precision */
6773 is_lo = es_lo, is_hi = es_hi;
6774
John Bowler6f55ee22011-06-11 07:28:06 -05006775 /* Attempt to output a meaningful error/warning message: the message
6776 * output depends on the background/composite operation being performed
6777 * because this changes what parameters were actually used above.
6778 */
John Bowlerd273ad22011-05-07 21:00:28 -05006779 {
John Bowler6f55ee22011-06-11 07:28:06 -05006780 size_t pos = 0;
6781 /* Need either 1/255 or 1/65535 precision here; 3 or 6 decimal
6782 * places. Just use outmax to work out which.
6783 */
6784 int precision = (outmax >= 1000 ? 6 : 3);
6785 int use_input=1, use_background=0, do_compose=0;
John Bowlerd273ad22011-05-07 21:00:28 -05006786 char msg[256];
6787
John Bowler6f55ee22011-06-11 07:28:06 -05006788 if (pass != 0)
6789 pos = safecat(msg, sizeof msg, pos, "\n\t");
John Bowlerd273ad22011-05-07 21:00:28 -05006790
John Bowler6f55ee22011-06-11 07:28:06 -05006791 /* Set up the various flags, the output_is_encoded flag above
6792 * is also used below. do_compose is just a double check.
6793 */
6794 switch (do_background)
6795 {
6796 case PNG_BACKGROUND_GAMMA_SCREEN:
6797 case PNG_BACKGROUND_GAMMA_FILE:
6798 case PNG_BACKGROUND_GAMMA_UNIQUE:
6799 use_background = (alpha >= 0 && alpha < 1);
6800 /*FALL THROUGH*/
6801# ifdef PNG_READ_ALPHA_MODE_SUPPORTED
6802 case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
6803 case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
6804 case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
6805# endif /* ALPHA_MODE_SUPPORTED */
6806 do_compose = (alpha >= 0 && alpha < 1);
6807 use_input = (alpha != 0);
6808 break;
6809
6810 default:
6811 break;
6812 }
6813
6814 /* Check the 'compose' flag */
6815 if (compose != do_compose)
6816 png_error(vi->pp, "internal error (compose)");
6817
6818 /* 'name' is the component name */
6819 pos = safecat(msg, sizeof msg, pos, name);
6820 pos = safecat(msg, sizeof msg, pos, "(");
6821 pos = safecatn(msg, sizeof msg, pos, id);
6822 if (use_input || pass != 0/*logging*/)
6823 {
6824 if (isbit != id)
6825 {
6826 /* sBIT has reduced the precision of the input: */
6827 pos = safecat(msg, sizeof msg, pos, ", sbit(");
6828 pos = safecatn(msg, sizeof msg, pos, vi->sbit);
6829 pos = safecat(msg, sizeof msg, pos, "): ");
6830 pos = safecatn(msg, sizeof msg, pos, isbit);
6831 }
6832 pos = safecat(msg, sizeof msg, pos, "/");
6833 /* The output is either "id/max" or "id sbit(sbit): isbit/max" */
6834 pos = safecatn(msg, sizeof msg, pos, vi->sbit_max);
6835 }
6836 pos = safecat(msg, sizeof msg, pos, ")");
6837
6838 /* A component may have been multiplied (in linear space) by the
6839 * alpha value, 'compose' says whether this is relevant.
6840 */
6841 if (compose || pass != 0)
6842 {
6843 /* If any form of composition is being done report our
6844 * calculated linear value here (the code above doesn't record
6845 * the input value before composition is performed, so what
6846 * gets reported is the value after composition.)
6847 */
6848 if (use_input || pass != 0)
6849 {
6850 if (vi->file_inverse > 0)
6851 {
6852 pos = safecat(msg, sizeof msg, pos, "^");
6853 pos = safecatd(msg, sizeof msg, pos, vi->file_inverse, 2);
6854 }
6855
6856 else
6857 pos = safecat(msg, sizeof msg, pos, "[linear]");
6858
6859 pos = safecat(msg, sizeof msg, pos, "*(alpha)");
6860 pos = safecatd(msg, sizeof msg, pos, alpha, precision);
6861 }
6862
6863 /* Now record the *linear* background value if it was used
6864 * (this function is not passed the original, non-linear,
6865 * value but it is contained in the test name.)
6866 */
6867 if (use_background)
6868 {
6869 pos = safecat(msg, sizeof msg, pos, use_input ? "+" : " ");
6870 pos = safecat(msg, sizeof msg, pos, "(background)");
6871 pos = safecatd(msg, sizeof msg, pos, background, precision);
6872 pos = safecat(msg, sizeof msg, pos, "*");
6873 pos = safecatd(msg, sizeof msg, pos, 1-alpha, precision);
6874 }
6875 }
6876
6877 /* Report the calculated value (input_sample) and the linearized
6878 * libpng value (output) unless this is just a component gamma
6879 * correction.
6880 */
6881 if (compose || alpha < 0 || pass != 0)
6882 {
6883 pos = safecat(msg, sizeof msg, pos,
6884 pass != 0 ? " =\n\t" : " = ");
6885 pos = safecatd(msg, sizeof msg, pos, input_sample, precision);
6886 pos = safecat(msg, sizeof msg, pos, " (libpng: ");
6887 pos = safecatd(msg, sizeof msg, pos, output, precision);
6888 pos = safecat(msg, sizeof msg, pos, ")");
6889
6890 /* Finally report the output gamma encoding, if any. */
6891 if (output_is_encoded)
6892 {
6893 pos = safecat(msg, sizeof msg, pos, " ^");
6894 pos = safecatd(msg, sizeof msg, pos, vi->screen_inverse, 2);
6895 pos = safecat(msg, sizeof msg, pos, "(to screen) =");
6896 }
6897
6898 else
6899 pos = safecat(msg, sizeof msg, pos, " [screen is linear] =");
6900 }
6901
6902 if ((!compose && alpha >= 0) || pass != 0)
6903 {
6904 if (pass != 0) /* logging */
6905 pos = safecat(msg, sizeof msg, pos, "\n\t[overall:");
6906
6907 /* This is the non-composition case, the internal linear
6908 * values are irrelevant (though the log below will reveal
6909 * them.) Output a much shorter warning/error message and report
6910 * the overall gamma correction.
6911 */
6912 if (vi->gamma_correction > 0)
6913 {
6914 pos = safecat(msg, sizeof msg, pos, " ^");
6915 pos = safecatd(msg, sizeof msg, pos, vi->gamma_correction, 2);
6916 pos = safecat(msg, sizeof msg, pos, "(gamma correction) =");
6917 }
6918
6919 else
6920 pos = safecat(msg, sizeof msg, pos,
6921 " [no gamma correction] =");
6922
6923 if (pass != 0)
6924 pos = safecat(msg, sizeof msg, pos, "]");
6925 }
6926
6927 /* This is our calculated encoded_sample which should (but does
6928 * not) match od:
6929 */
6930 pos = safecat(msg, sizeof msg, pos, pass != 0 ? "\n\t" : " ");
6931 pos = safecatd(msg, sizeof msg, pos, is_lo, 1);
6932 pos = safecat(msg, sizeof msg, pos, " < ");
6933 pos = safecatd(msg, sizeof msg, pos, encoded_sample, 1);
6934 pos = safecat(msg, sizeof msg, pos, " (libpng: ");
6935 pos = safecatn(msg, sizeof msg, pos, od);
6936 pos = safecat(msg, sizeof msg, pos, ")");
6937 pos = safecat(msg, sizeof msg, pos, "/");
6938 pos = safecatn(msg, sizeof msg, pos, outmax);
6939 pos = safecat(msg, sizeof msg, pos, " < ");
6940 pos = safecatd(msg, sizeof msg, pos, is_hi, 1);
6941
6942 if (pass == 0) /* The error condition */
6943 {
6944# ifdef PNG_WARNINGS_SUPPORTED
6945 png_warning(vi->pp, msg);
6946# else
6947 store_warning(vi->pp, msg);
6948# endif
6949 }
6950
6951 else /* logging this value */
6952 store_verbose(&vi->dp->pm->this, vi->pp, pass, msg);
John Bowlerd273ad22011-05-07 21:00:28 -05006953 }
6954 }
6955 }
6956
6957 return i;
6958}
6959
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006960static void
John Bowler9994f252011-05-15 18:52:39 -05006961gamma_image_validate(gamma_display *dp, png_structp pp, png_infop pi)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006962{
6963 /* Get some constants derived from the input and output file formats: */
John Bowler9994f252011-05-15 18:52:39 -05006964 PNG_CONST png_store* PNG_CONST ps = dp->this.ps;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006965 PNG_CONST png_byte in_ct = dp->this.colour_type;
6966 PNG_CONST png_byte in_bd = dp->this.bit_depth;
6967 PNG_CONST png_uint_32 w = dp->this.w;
6968 PNG_CONST png_uint_32 h = dp->this.h;
6969 PNG_CONST size_t cbRow = dp->this.cbRow;
6970 PNG_CONST png_byte out_ct = png_get_color_type(pp, pi);
6971 PNG_CONST png_byte out_bd = png_get_bit_depth(pp, pi);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006972
6973 /* There are three sources of error, firstly the quantization in the
6974 * file encoding, determined by sbit and/or the file depth, secondly
6975 * the output (screen) gamma and thirdly the output file encoding.
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006976 *
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006977 * Since this API receives the screen and file gamma in double
6978 * precision it is possible to calculate an exact answer given an input
6979 * pixel value. Therefore we assume that the *input* value is exact -
6980 * sample/maxsample - calculate the corresponding gamma corrected
6981 * output to the limits of double precision arithmetic and compare with
6982 * what libpng returns.
6983 *
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006984 * Since the library must quantize the output to 8 or 16 bits there is
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006985 * a fundamental limit on the accuracy of the output of +/-.5 - this
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006986 * quantization limit is included in addition to the other limits
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006987 * specified by the paramaters to the API. (Effectively, add .5
6988 * everywhere.)
6989 *
6990 * The behavior of the 'sbit' paramter is defined by section 12.5
6991 * (sample depth scaling) of the PNG spec. That section forces the
6992 * decoder to assume that the PNG values have been scaled if sBIT is
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006993 * present:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006994 *
Glenn Randers-Pehrsonbc363ec2010-10-12 21:17:00 -05006995 * png-sample = floor( input-sample * (max-out/max-in) + .5);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006996 *
6997 * This means that only a subset of the possible PNG values should
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06006998 * appear in the input. However, the spec allows the encoder to use a
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05006999 * variety of approximations to the above and doesn't require any
7000 * restriction of the values produced.
7001 *
7002 * Nevertheless the spec requires that the upper 'sBIT' bits of the
7003 * value stored in a PNG file be the original sample bits.
7004 * Consequently the code below simply scales the top sbit bits by
7005 * (1<<sbit)-1 to obtain an original sample value.
7006 *
7007 * Because there is limited precision in the input it is arguable that
7008 * an acceptable result is any valid result from input-.5 to input+.5.
John Bowlerd273ad22011-05-07 21:00:28 -05007009 * The basic tests below do not do this, however if 'use_input_precision'
7010 * is set a subsequent test is performed below.
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007011 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007012 PNG_CONST unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U;
John Bowlerd273ad22011-05-07 21:00:28 -05007013 int processing;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007014 png_uint_32 y;
John Bowler5441e182011-05-18 18:57:12 -05007015 PNG_CONST store_palette_entry *in_palette = dp->this.palette;
7016 PNG_CONST int in_is_transparent = dp->this.is_transparent;
7017 int out_npalette = -1;
7018 int out_is_transparent = 0; /* Just refers to the palette case */
7019 store_palette out_palette;
John Bowlerd273ad22011-05-07 21:00:28 -05007020 validate_info vi;
7021
John Bowler9994f252011-05-15 18:52:39 -05007022 /* Check for row overwrite errors */
7023 store_image_check(dp->this.ps, pp, 0);
7024
John Bowler5441e182011-05-18 18:57:12 -05007025 /* Supply the input and output sample depths here - 8 for an indexed image,
7026 * otherwise the bit depth.
7027 */
7028 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 -05007029
John Bowler5441e182011-05-18 18:57:12 -05007030 processing = (vi.gamma_correction > 0 && !dp->threshold_test)
John Bowlered4d32b2011-05-11 23:02:28 -05007031 || in_bd != out_bd || in_ct != out_ct || vi.do_background;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007032
John Bowler5441e182011-05-18 18:57:12 -05007033 /* TODO: FIX THIS: MAJOR BUG! If the transformations all happen inside
7034 * the palette there is no way of finding out, because libpng fails to
7035 * update the palette on png_read_update_info. Indeed, libpng doesn't
7036 * even do the required work until much later, when it doesn't have any
7037 * info pointer. Oops. For the moment 'processing' is turned off if
7038 * out_ct is palette.
7039 */
7040 if (in_ct == 3 && out_ct == 3)
7041 processing = 0;
7042
7043 if (processing && out_ct == 3)
7044 out_is_transparent = read_palette(out_palette, &out_npalette, pp, pi);
7045
John Bowler9994f252011-05-15 18:52:39 -05007046 for (y=0; y<h; ++y)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007047 {
John Bowler9994f252011-05-15 18:52:39 -05007048 png_const_bytep pRow = store_image_row(ps, pp, 0, y);
John Bowler660c6e42010-12-19 06:22:23 -06007049 png_byte std[STANDARD_ROWMAX];
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007050
John Bowler660c6e42010-12-19 06:22:23 -06007051 transform_row(pp, std, in_ct, in_bd, y);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007052
7053 if (processing)
7054 {
John Bowlerd273ad22011-05-07 21:00:28 -05007055 unsigned int x;
7056
7057 for (x=0; x<w; ++x)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007058 {
John Bowlerd273ad22011-05-07 21:00:28 -05007059 double alpha = 1; /* serves as a flag value */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007060
John Bowler5441e182011-05-18 18:57:12 -05007061 /* Record the palette index for index images. */
7062 PNG_CONST unsigned int in_index =
7063 in_ct == 3 ? sample(std, 3, in_bd, x, 0) : 256;
7064 PNG_CONST unsigned int out_index =
7065 out_ct == 3 ? sample(std, 3, out_bd, x, 0) : 256;
7066
John Bowlerd273ad22011-05-07 21:00:28 -05007067 /* Handle input alpha - png_set_background will cause the output
7068 * alpha to disappear so there is nothing to check.
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007069 */
John Bowler5441e182011-05-18 18:57:12 -05007070 if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 || (in_ct == 3 &&
7071 in_is_transparent))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007072 {
John Bowler5441e182011-05-18 18:57:12 -05007073 PNG_CONST unsigned int input_alpha = in_ct == 3 ?
7074 dp->this.palette[in_index].alpha :
John Bowlerd273ad22011-05-07 21:00:28 -05007075 sample(std, in_ct, in_bd, x, samples_per_pixel);
7076
John Bowler5441e182011-05-18 18:57:12 -05007077 unsigned int output_alpha = 65536 /* as a flag value */;
John Bowler6f55ee22011-06-11 07:28:06 -05007078
John Bowler5441e182011-05-18 18:57:12 -05007079 if (out_ct == 3)
John Bowlerd273ad22011-05-07 21:00:28 -05007080 {
John Bowler5441e182011-05-18 18:57:12 -05007081 if (out_is_transparent)
7082 output_alpha = out_palette[out_index].alpha;
7083 }
7084
7085 else if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0)
7086 output_alpha = sample(pRow, out_ct, out_bd, x,
7087 samples_per_pixel);
7088
7089 if (output_alpha != 65536)
7090 alpha = gamma_component_validate("alpha", &vi, input_alpha,
7091 output_alpha, -1/*alpha*/, 0/*background*/);
7092
7093 else /* no alpha in output */
7094 {
7095 /* This is a copy of the calculation of 'i' above in order to
7096 * have the alpha value to use in the background calculation.
7097 */
John Bowlerd273ad22011-05-07 21:00:28 -05007098 alpha = input_alpha >> vi.isbit_shift;
7099 alpha /= vi.sbit_max;
7100 }
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007101 }
7102
John Bowlerd273ad22011-05-07 21:00:28 -05007103 /* Handle greyscale or RGB components. */
7104 if ((in_ct & PNG_COLOR_MASK_COLOR) == 0) /* greyscale */
7105 (void)gamma_component_validate("gray", &vi,
7106 sample(std, in_ct, in_bd, x, 0),
7107 sample(pRow, out_ct, out_bd, x, 0), alpha/*component*/,
7108 vi.background_red);
John Bowler5441e182011-05-18 18:57:12 -05007109 else /* RGB or palette */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007110 {
John Bowlerd273ad22011-05-07 21:00:28 -05007111 (void)gamma_component_validate("red", &vi,
John Bowler5441e182011-05-18 18:57:12 -05007112 in_ct == 3 ? in_palette[in_index].red :
7113 sample(std, in_ct, in_bd, x, 0),
7114 out_ct == 3 ? out_palette[out_index].red :
7115 sample(pRow, out_ct, out_bd, x, 0),
7116 alpha/*component*/, vi.background_red);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007117
John Bowlerd273ad22011-05-07 21:00:28 -05007118 (void)gamma_component_validate("green", &vi,
John Bowler5441e182011-05-18 18:57:12 -05007119 in_ct == 3 ? in_palette[in_index].green :
7120 sample(std, in_ct, in_bd, x, 1),
7121 out_ct == 3 ? out_palette[out_index].green :
7122 sample(pRow, out_ct, out_bd, x, 1),
7123 alpha/*component*/, vi.background_green);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007124
John Bowlerd273ad22011-05-07 21:00:28 -05007125 (void)gamma_component_validate("blue", &vi,
John Bowler5441e182011-05-18 18:57:12 -05007126 in_ct == 3 ? in_palette[in_index].blue :
7127 sample(std, in_ct, in_bd, x, 2),
7128 out_ct == 3 ? out_palette[out_index].blue :
7129 sample(pRow, out_ct, out_bd, x, 2),
7130 alpha/*component*/, vi.background_blue);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007131 }
7132 }
7133 }
7134
John Bowlered4d32b2011-05-11 23:02:28 -05007135 else if (memcmp(std, pRow, cbRow) != 0)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007136 {
7137 char msg[64];
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007138
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007139 /* No transform is expected on the threshold tests. */
7140 sprintf(msg, "gamma: below threshold row %d changed", y);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007141
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007142 png_error(pp, msg);
7143 }
7144 } /* row (y) loop */
7145
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007146 dp->this.ps->validated = 1;
7147}
7148
7149static void
7150gamma_end(png_structp pp, png_infop pi)
7151{
7152 gamma_display *dp = png_get_progressive_ptr(pp);
7153
John Bowler9994f252011-05-15 18:52:39 -05007154 if (!dp->this.speed)
7155 gamma_image_validate(dp, pp, pi);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007156}
7157
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05007158/* A single test run checking a gamma transformation.
7159 *
7160 * maxabs: maximum absolute error as a fraction
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007161 * maxout: maximum output error in the output units
7162 * maxpc: maximum percentage error (as a percentage)
7163 */
7164static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007165gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn,
John Bowler9994f252011-05-15 18:52:39 -05007166 PNG_CONST png_byte bit_depthIn, PNG_CONST int palette_numberIn,
7167 PNG_CONST int interlace_typeIn,
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007168 PNG_CONST double file_gammaIn, PNG_CONST double screen_gammaIn,
7169 PNG_CONST png_byte sbitIn, PNG_CONST int threshold_testIn,
John Bowler9994f252011-05-15 18:52:39 -05007170 PNG_CONST char *name,
John Bowlerd273ad22011-05-07 21:00:28 -05007171 PNG_CONST int use_input_precisionIn, PNG_CONST int strip16In,
7172 PNG_CONST int expand16In, PNG_CONST int do_backgroundIn,
7173 PNG_CONST png_color_16 *bkgd_colorIn, double bkgd_gammaIn)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007174{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007175 gamma_display d;
7176 context(&pmIn->this, fault);
7177
John Bowler660c6e42010-12-19 06:22:23 -06007178 gamma_display_init(&d, pmIn, FILEID(colour_typeIn, bit_depthIn,
John Bowler9994f252011-05-15 18:52:39 -05007179 palette_numberIn, interlace_typeIn, 0, 0, 0),
7180 file_gammaIn, screen_gammaIn, sbitIn,
7181 threshold_testIn, use_input_precisionIn, strip16In,
John Bowlerd273ad22011-05-07 21:00:28 -05007182 expand16In, do_backgroundIn, bkgd_colorIn, bkgd_gammaIn);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007183
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007184 Try
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007185 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007186 png_structp pp;
7187 png_infop pi;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007188 gamma_modification gamma_mod;
7189 srgb_modification srgb_mod;
7190 sbit_modification sbit_mod;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007191
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007192 /* Make an appropriate modifier to set the PNG file gamma to the
7193 * given gamma value and the sBIT chunk to the given precision.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007194 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007195 d.pm->modifications = NULL;
7196 gamma_modification_init(&gamma_mod, d.pm, d.file_gamma);
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06007197 srgb_modification_init(&srgb_mod, d.pm, 127 /*delete*/);
John Bowler5441e182011-05-18 18:57:12 -05007198 if (d.sbit > 0)
7199 sbit_modification_init(&sbit_mod, d.pm, d.sbit);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007200
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007201 modification_reset(d.pm->modifications);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007202
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05007203 /* Get a png_struct for writing the image. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007204 pp = set_modifier_for_read(d.pm, &pi, d.this.id, name);
John Bowler9994f252011-05-15 18:52:39 -05007205 standard_palette_init(&d.this);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05007206
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007207 /* Introduce the correct read function. */
7208 if (d.pm->this.progressive)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007209 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007210 /* Share the row function with the standard implementation. */
7211 png_set_progressive_read_fn(pp, &d, gamma_info, progressive_row,
7212 gamma_end);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007213
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007214 /* Now feed data into the reader until we reach the end: */
7215 modifier_progressive_read(d.pm, pp, pi);
7216 }
7217 else
7218 {
7219 /* modifier_read expects a png_modifier* */
7220 png_set_read_fn(pp, d.pm, modifier_read);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007221
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007222 /* Check the header values: */
7223 png_read_info(pp, pi);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007224
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007225 /* Process the 'info' requirements. Only one image is generated */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007226 gamma_info_imp(&d, pp, pi);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007227
John Bowler9994f252011-05-15 18:52:39 -05007228 sequential_row(&d.this, pp, pi, -1, 0);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007229
John Bowler9994f252011-05-15 18:52:39 -05007230 if (!d.this.speed)
7231 gamma_image_validate(&d, pp, pi);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007232 }
7233
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007234 modifier_reset(d.pm);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007235
John Bowler9994f252011-05-15 18:52:39 -05007236 if (d.pm->log && !d.threshold_test && !d.this.speed)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007237 fprintf(stderr, "%d bit %s %s: max error %f (%.2g, %2g%%)\n",
John Bowlerd273ad22011-05-07 21:00:28 -05007238 d.this.bit_depth, colour_types[d.this.colour_type], name,
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007239 d.maxerrout, d.maxerrabs, 100*d.maxerrpc);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007240
7241 /* Log the summary values too. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007242 if (d.this.colour_type == 0 || d.this.colour_type == 4)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007243 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007244 switch (d.this.bit_depth)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05007245 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007246 case 1:
7247 break;
7248
7249 case 2:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007250 if (d.maxerrout > d.pm->error_gray_2)
7251 d.pm->error_gray_2 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007252
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007253 break;
7254
7255 case 4:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007256 if (d.maxerrout > d.pm->error_gray_4)
7257 d.pm->error_gray_4 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007258
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007259 break;
7260
7261 case 8:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007262 if (d.maxerrout > d.pm->error_gray_8)
7263 d.pm->error_gray_8 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007264
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007265 break;
7266
7267 case 16:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007268 if (d.maxerrout > d.pm->error_gray_16)
7269 d.pm->error_gray_16 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007270
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007271 break;
7272
7273 default:
7274 png_error(pp, "bad bit depth (internal: 1)");
7275 }
7276 }
7277
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007278 else if (d.this.colour_type == 2 || d.this.colour_type == 6)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007279 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007280 switch (d.this.bit_depth)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007281 {
7282 case 8:
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007283
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007284 if (d.maxerrout > d.pm->error_color_8)
7285 d.pm->error_color_8 = d.maxerrout;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007286
7287 break;
7288
7289 case 16:
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007290
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05007291 if (d.maxerrout > d.pm->error_color_16)
7292 d.pm->error_color_16 = d.maxerrout;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007293
7294 break;
7295
7296 default:
7297 png_error(pp, "bad bit depth (internal: 2)");
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05007298 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007299 }
John Bowler1921e6d2011-05-16 20:57:54 -05007300
7301 else if (d.this.colour_type == 3)
7302 {
7303 if (d.maxerrout > d.pm->error_indexed)
7304 d.pm->error_indexed = d.maxerrout;
7305 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007306 }
7307
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007308 Catch(fault)
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05007309 modifier_reset((png_modifier*)fault);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007310}
7311
7312static void gamma_threshold_test(png_modifier *pm, png_byte colour_type,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05007313 png_byte bit_depth, int interlace_type, double file_gamma,
7314 double screen_gamma)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007315{
7316 size_t pos = 0;
7317 char name[64];
7318 pos = safecat(name, sizeof name, pos, "threshold ");
7319 pos = safecatd(name, sizeof name, pos, file_gamma, 3);
7320 pos = safecat(name, sizeof name, pos, "/");
7321 pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
7322
John Bowler9994f252011-05-15 18:52:39 -05007323 (void)gamma_test(pm, colour_type, bit_depth, 0/*palette*/, interlace_type,
John Bowler5441e182011-05-18 18:57:12 -05007324 file_gamma, screen_gamma, 0/*sBIT*/, 1/*threshold test*/, name,
John Bowler9994f252011-05-15 18:52:39 -05007325 0 /*no input precision*/,
John Bowlerd273ad22011-05-07 21:00:28 -05007326 0 /*no strip16*/, 0 /*no expand16*/, 0 /*no background*/, 0 /*hence*/,
7327 0 /*no background gamma*/);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007328}
7329
7330static void
7331perform_gamma_threshold_tests(png_modifier *pm)
7332{
7333 png_byte colour_type = 0;
7334 png_byte bit_depth = 0;
John Bowler9994f252011-05-15 18:52:39 -05007335 int palette_number = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007336
John Bowler9994f252011-05-15 18:52:39 -05007337 /* Don't test more than one instance of each palette - it's pointless, in
7338 * fact this test is somewhat excessive since libpng doesn't make this
7339 * decision based on colour type or bit depth!
7340 */
7341 while (next_format(&colour_type, &bit_depth, &palette_number))
7342 if (palette_number == 0)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007343 {
John Bowler168a4332011-01-16 19:32:22 -06007344 double test_gamma = 1.0;
7345 while (test_gamma >= .4)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007346 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05007347 /* There's little point testing the interlacing vs non-interlacing,
7348 * but this can be set from the command line.
7349 */
7350 gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
John Bowler168a4332011-01-16 19:32:22 -06007351 test_gamma, 1/test_gamma);
7352 test_gamma *= .95;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007353 }
7354
7355 /* And a special test for sRGB */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05007356 gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
7357 .45455, 2.2);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007358
7359 if (fail(pm))
7360 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007361 }
7362}
7363
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05007364static void gamma_transform_test(png_modifier *pm,
7365 PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth,
John Bowler9994f252011-05-15 18:52:39 -05007366 PNG_CONST int palette_number,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05007367 PNG_CONST int interlace_type, PNG_CONST double file_gamma,
John Bowler9994f252011-05-15 18:52:39 -05007368 PNG_CONST double screen_gamma, PNG_CONST png_byte sbit,
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05007369 PNG_CONST int use_input_precision, PNG_CONST int strip16)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007370{
7371 size_t pos = 0;
7372 char name[64];
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007373
John Bowler5441e182011-05-18 18:57:12 -05007374 if (sbit != bit_depth && sbit != 0)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007375 {
7376 pos = safecat(name, sizeof name, pos, "sbit(");
7377 pos = safecatn(name, sizeof name, pos, sbit);
7378 pos = safecat(name, sizeof name, pos, ") ");
7379 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007380
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007381 else
7382 pos = safecat(name, sizeof name, pos, "gamma ");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007383
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007384 if (strip16)
7385 pos = safecat(name, sizeof name, pos, "16to8 ");
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007386
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007387 pos = safecatd(name, sizeof name, pos, file_gamma, 3);
7388 pos = safecat(name, sizeof name, pos, "->");
7389 pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
7390
John Bowler9994f252011-05-15 18:52:39 -05007391 gamma_test(pm, colour_type, bit_depth, palette_number, interlace_type,
7392 file_gamma, screen_gamma, sbit, 0, name, use_input_precision,
7393 strip16, pm->test_gamma_expand16, 0 , 0, 0);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007394}
7395
John Bowler9994f252011-05-15 18:52:39 -05007396static void perform_gamma_transform_tests(png_modifier *pm)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007397{
7398 png_byte colour_type = 0;
7399 png_byte bit_depth = 0;
John Bowler9994f252011-05-15 18:52:39 -05007400 int palette_number = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007401
John Bowler9994f252011-05-15 18:52:39 -05007402 while (next_format(&colour_type, &bit_depth, &palette_number))
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007403 {
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05007404 unsigned int i, j;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007405
7406 for (i=0; i<pm->ngammas; ++i) for (j=0; j<pm->ngammas; ++j) if (i != j)
7407 {
John Bowler9994f252011-05-15 18:52:39 -05007408 gamma_transform_test(pm, colour_type, bit_depth, palette_number,
John Bowler5441e182011-05-18 18:57:12 -05007409 pm->interlace_type, 1/pm->gammas[i], pm->gammas[j], 0/*sBIT*/,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06007410 pm->use_input_precision, 0 /*do not strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007411
7412 if (fail(pm))
7413 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007414 }
7415 }
7416}
7417
John Bowler9994f252011-05-15 18:52:39 -05007418static void perform_gamma_sbit_tests(png_modifier *pm)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007419{
7420 png_byte sbit;
7421
7422 /* The only interesting cases are colour and grayscale, alpha is ignored here
John Bowler9994f252011-05-15 18:52:39 -05007423 * for overall speed. Only bit depths where sbit is less than the bit depth
7424 * are tested.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007425 */
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05007426 for (sbit=pm->sbitlow; sbit<(1<<READ_BDHI); ++sbit)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007427 {
John Bowler9994f252011-05-15 18:52:39 -05007428 png_byte colour_type, bit_depth;
7429 int npalette;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007430
John Bowler9994f252011-05-15 18:52:39 -05007431 colour_type = bit_depth = 0;
7432 npalette = 0;
7433
7434 while (next_format(&colour_type, &bit_depth, &npalette))
7435 if ((colour_type & PNG_COLOR_MASK_ALPHA) == 0 &&
7436 ((colour_type == 3 && sbit < 8) ||
7437 (colour_type != 3 && sbit < bit_depth)))
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007438 {
John Bowler9994f252011-05-15 18:52:39 -05007439 unsigned int i;
7440
7441 for (i=0; i<pm->ngammas; ++i)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05007442 {
John Bowler9994f252011-05-15 18:52:39 -05007443 unsigned int j;
7444
7445 for (j=0; j<pm->ngammas; ++j) if (i != j)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007446 {
John Bowler9994f252011-05-15 18:52:39 -05007447 gamma_transform_test(pm, colour_type, bit_depth, npalette,
7448 pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
7449 sbit, pm->use_input_precision_sbit, 0 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007450
7451 if (fail(pm))
7452 return;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007453 }
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05007454 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007455 }
7456 }
7457}
7458
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05007459/* Note that this requires a 16 bit source image but produces 8 bit output, so
John Bowler9994f252011-05-15 18:52:39 -05007460 * we only need the 16bit write support, but the 16 bit images are only
7461 * generated if DO_16BIT is defined.
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05007462 */
John Bowler9994f252011-05-15 18:52:39 -05007463#ifdef DO_16BIT
7464static void perform_gamma_strip16_tests(png_modifier *pm)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007465{
7466# ifndef PNG_MAX_GAMMA_8
7467# define PNG_MAX_GAMMA_8 11
7468# endif
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06007469 /* Include the alpha cases here. Note that sbit matches the internal value
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007470 * used by the library - otherwise we will get spurious errors from the
7471 * internal sbit style approximation.
7472 *
Glenn Randers-Pehrson233357e2010-07-29 21:49:38 -05007473 * The threshold test is here because otherwise the 16 to 8 conversion will
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007474 * proceed *without* gamma correction, and the tests above will fail (but not
7475 * by much) - this could be fixed, it only appears with the -g option.
7476 */
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05007477 unsigned int i, j;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007478 for (i=0; i<pm->ngammas; ++i)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007479 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007480 for (j=0; j<pm->ngammas; ++j)
7481 {
7482 if (i != j &&
7483 fabs(pm->gammas[j]/pm->gammas[i]-1) >= PNG_GAMMA_THRESHOLD)
7484 {
John Bowler9994f252011-05-15 18:52:39 -05007485 gamma_transform_test(pm, 0, 16, 0, pm->interlace_type,
7486 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
7487 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007488
7489 if (fail(pm))
7490 return;
7491
John Bowler9994f252011-05-15 18:52:39 -05007492 gamma_transform_test(pm, 2, 16, 0, pm->interlace_type,
7493 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
7494 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007495
7496 if (fail(pm))
7497 return;
7498
John Bowler9994f252011-05-15 18:52:39 -05007499 gamma_transform_test(pm, 4, 16, 0, pm->interlace_type,
7500 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
7501 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007502
7503 if (fail(pm))
7504 return;
7505
John Bowler9994f252011-05-15 18:52:39 -05007506 gamma_transform_test(pm, 6, 16, 0, pm->interlace_type,
7507 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
7508 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007509
7510 if (fail(pm))
7511 return;
7512 }
7513 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007514 }
7515}
Glenn Randers-Pehrsonfded04f2010-08-27 14:21:21 -05007516#endif /* 16 to 8 bit conversion */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007517
John Bowler9994f252011-05-15 18:52:39 -05007518#if defined PNG_READ_BACKGROUND_SUPPORTED ||\
7519 defined PNG_READ_ALPHA_MODE_SUPPORTED
John Bowlerd273ad22011-05-07 21:00:28 -05007520static void gamma_composition_test(png_modifier *pm,
7521 PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth,
John Bowler9994f252011-05-15 18:52:39 -05007522 PNG_CONST int palette_number,
John Bowlerd273ad22011-05-07 21:00:28 -05007523 PNG_CONST int interlace_type, PNG_CONST double file_gamma,
John Bowler9994f252011-05-15 18:52:39 -05007524 PNG_CONST double screen_gamma,
John Bowlerd273ad22011-05-07 21:00:28 -05007525 PNG_CONST int use_input_precision, PNG_CONST int do_background,
7526 PNG_CONST int expand_16)
7527{
7528 size_t pos = 0;
7529 png_const_charp base;
7530 double bg;
7531 char name[128];
7532 png_color_16 background;
7533
7534 /* Make up a name and get an appropriate background gamma value. */
7535 switch (do_background)
7536 {
7537 default:
John Bowler6f55ee22011-06-11 07:28:06 -05007538 base = "";
John Bowlerd273ad22011-05-07 21:00:28 -05007539 bg = 4; /* should not be used */
7540 break;
7541 case PNG_BACKGROUND_GAMMA_SCREEN:
John Bowler6f55ee22011-06-11 07:28:06 -05007542 base = " bckg(Screen):";
John Bowlerd273ad22011-05-07 21:00:28 -05007543 bg = 1/screen_gamma;
7544 break;
7545 case PNG_BACKGROUND_GAMMA_FILE:
John Bowler6f55ee22011-06-11 07:28:06 -05007546 base = " bckg(File):";
John Bowlerd273ad22011-05-07 21:00:28 -05007547 bg = file_gamma;
7548 break;
7549 case PNG_BACKGROUND_GAMMA_UNIQUE:
John Bowler6f55ee22011-06-11 07:28:06 -05007550 base = " bckg(Unique):";
John Bowlerd273ad22011-05-07 21:00:28 -05007551 /* This tests the handling of a unique value, the math is such that the
7552 * value tends to be <1, but is neither screen nor file (even if they
7553 * match!)
7554 */
7555 bg = (file_gamma + screen_gamma) / 3;
7556 break;
7557#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
7558 case ALPHA_MODE_OFFSET + PNG_ALPHA_PNG:
John Bowler6f55ee22011-06-11 07:28:06 -05007559 base = " alpha(PNG)";
John Bowlerd273ad22011-05-07 21:00:28 -05007560 bg = 4; /* should not be used */
7561 break;
7562 case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
John Bowler6f55ee22011-06-11 07:28:06 -05007563 base = " alpha(Porter-Duff)";
John Bowlerd273ad22011-05-07 21:00:28 -05007564 bg = 4; /* should not be used */
7565 break;
7566 case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
John Bowler6f55ee22011-06-11 07:28:06 -05007567 base = " alpha(Optimized)";
John Bowlerd273ad22011-05-07 21:00:28 -05007568 bg = 4; /* should not be used */
7569 break;
7570 case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
John Bowler6f55ee22011-06-11 07:28:06 -05007571 base = " alpha(Broken)";
John Bowlerd273ad22011-05-07 21:00:28 -05007572 bg = 4; /* should not be used */
7573 break;
7574#endif
7575 }
7576
7577 /* Use random background values - the background is always presented in the
7578 * output space (8 or 16 bit components).
7579 */
7580 if (expand_16 || bit_depth == 16)
7581 {
7582 png_uint_32 r = random_32();
7583
7584 background.red = (png_uint_16)r;
7585 background.green = (png_uint_16)(r >> 16);
7586 r = random_32();
7587 background.blue = (png_uint_16)r;
7588 background.gray = (png_uint_16)(r >> 16);
7589 }
7590
7591 else /* 8 bit colors */
7592 {
7593 png_uint_32 r = random_32();
7594
7595 background.red = (png_byte)r;
7596 background.green = (png_byte)(r >> 8);
7597 background.blue = (png_byte)(r >> 16);
7598 background.gray = (png_byte)(r >> 24);
7599 }
7600
7601 background.index = 193; /* rgb(193,193,193) to detect errors */
7602 if (!(colour_type & PNG_COLOR_MASK_COLOR))
7603 {
7604 /* Grayscale input, we do not convert to RGB (TBD), so we must set the
7605 * background to gray - else libpng seems to fail.
7606 */
7607 background.red = background.green = background.blue = background.gray;
7608 }
7609
John Bowler6f55ee22011-06-11 07:28:06 -05007610 pos = safecat(name, sizeof name, pos, "gamma ");
John Bowlerd273ad22011-05-07 21:00:28 -05007611 pos = safecatd(name, sizeof name, pos, file_gamma, 3);
John Bowler6f55ee22011-06-11 07:28:06 -05007612 pos = safecat(name, sizeof name, pos, "->");
7613 pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
7614
7615 pos = safecat(name, sizeof name, pos, base);
John Bowlerd273ad22011-05-07 21:00:28 -05007616 if (do_background < ALPHA_MODE_OFFSET)
7617 {
7618 /* Include the background color and gamma in the name: */
John Bowler6f55ee22011-06-11 07:28:06 -05007619 pos = safecat(name, sizeof name, pos, "(");
John Bowlerd273ad22011-05-07 21:00:28 -05007620 /* This assumes no expand gray->rgb - the current code won't handle that!
7621 */
7622 if (colour_type & PNG_COLOR_MASK_COLOR)
7623 {
7624 pos = safecatn(name, sizeof name, pos, background.red);
7625 pos = safecat(name, sizeof name, pos, ",");
John Bowler5441e182011-05-18 18:57:12 -05007626 pos = safecatn(name, sizeof name, pos, background.green);
John Bowlerd273ad22011-05-07 21:00:28 -05007627 pos = safecat(name, sizeof name, pos, ",");
7628 pos = safecatn(name, sizeof name, pos, background.blue);
7629 }
7630 else
7631 pos = safecatn(name, sizeof name, pos, background.gray);
7632 pos = safecat(name, sizeof name, pos, ")^");
7633 pos = safecatd(name, sizeof name, pos, bg, 3);
7634 }
John Bowlerd273ad22011-05-07 21:00:28 -05007635
John Bowler9994f252011-05-15 18:52:39 -05007636 gamma_test(pm, colour_type, bit_depth, palette_number, interlace_type,
John Bowler5441e182011-05-18 18:57:12 -05007637 file_gamma, screen_gamma, 0/*sBIT*/, 0, name, use_input_precision,
John Bowlerd273ad22011-05-07 21:00:28 -05007638 0/*strip 16*/, expand_16, do_background, &background, bg);
7639}
7640
7641
7642static void
John Bowler9994f252011-05-15 18:52:39 -05007643perform_gamma_composition_tests(png_modifier *pm, int do_background,
John Bowlerd273ad22011-05-07 21:00:28 -05007644 int expand_16)
7645{
7646 png_byte colour_type = 0;
7647 png_byte bit_depth = 0;
John Bowler9994f252011-05-15 18:52:39 -05007648 int palette_number = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05007649
John Bowler9994f252011-05-15 18:52:39 -05007650 /* Skip the non-alpha cases - there is no setting of a transparency colour at
7651 * present.
John Bowlerd273ad22011-05-07 21:00:28 -05007652 */
John Bowler9994f252011-05-15 18:52:39 -05007653 while (next_format(&colour_type, &bit_depth, &palette_number))
7654 if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0)
John Bowlerd273ad22011-05-07 21:00:28 -05007655 {
7656 unsigned int i, j;
7657
7658 /* Don't skip the i==j case here - it's relevant. */
7659 for (i=0; i<pm->ngammas; ++i) for (j=0; j<pm->ngammas; ++j)
7660 {
John Bowler9994f252011-05-15 18:52:39 -05007661 gamma_composition_test(pm, colour_type, bit_depth, palette_number,
7662 pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
John Bowler5441e182011-05-18 18:57:12 -05007663 pm->use_input_precision, do_background, expand_16);
John Bowlerd273ad22011-05-07 21:00:28 -05007664
7665 if (fail(pm))
7666 return;
7667 }
7668 }
7669}
7670#endif /* READ_BACKGROUND || READ_ALPHA_MODE */
7671
7672static void
7673init_gamma_errors(png_modifier *pm)
7674{
John Bowler1921e6d2011-05-16 20:57:54 -05007675 pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0;
7676 pm->error_color_8 = 0;
7677 pm->error_indexed = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05007678 pm->error_gray_16 = pm->error_color_16 = 0;
7679}
7680
7681static void
7682summarize_gamma_errors(png_modifier *pm, png_const_charp who, int low_bit_depth)
7683{
7684 if (who)
7685 printf("Gamma correction with %s:\n", who);
7686
7687 if (low_bit_depth)
7688 {
7689 printf(" 2 bit gray: %.5f\n", pm->error_gray_2);
7690 printf(" 4 bit gray: %.5f\n", pm->error_gray_4);
7691 printf(" 8 bit gray: %.5f\n", pm->error_gray_8);
7692 printf(" 8 bit color: %.5f\n", pm->error_color_8);
John Bowler1921e6d2011-05-16 20:57:54 -05007693 printf(" indexed: %.5f\n", pm->error_indexed);
John Bowlerd273ad22011-05-07 21:00:28 -05007694 }
7695
7696#ifdef DO_16BIT
7697 printf(" 16 bit gray: %.5f\n", pm->error_gray_16);
7698 printf(" 16 bit color: %.5f\n", pm->error_color_16);
7699#endif
7700}
7701
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007702static void
John Bowler9994f252011-05-15 18:52:39 -05007703perform_gamma_test(png_modifier *pm, int summary)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007704{
John Bowler5441e182011-05-18 18:57:12 -05007705 /*TODO: remove this*/
7706 /* Save certain values for the temporary overrides below. */
7707 unsigned int calculations_use_input_precision =
7708 pm->calculations_use_input_precision;
7709 double maxout8 = pm->maxout8;
7710
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007711 /* First some arbitrary no-transform tests: */
John Bowler9994f252011-05-15 18:52:39 -05007712 if (!pm->this.speed && pm->test_gamma_threshold)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007713 {
7714 perform_gamma_threshold_tests(pm);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05007715
7716 if (fail(pm))
7717 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007718 }
7719
7720 /* Now some real transforms. */
John Bowlerf21a0d02011-01-23 23:55:19 -06007721 if (pm->test_gamma_transform)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007722 {
John Bowlerd273ad22011-05-07 21:00:28 -05007723 init_gamma_errors(pm);
John Bowler6f55ee22011-06-11 07:28:06 -05007724 /*TODO: remove this. Necessary because the current libpng
John Bowler5441e182011-05-18 18:57:12 -05007725 * implementation works in 8 bits:
7726 */
7727 if (pm->test_gamma_expand16)
7728 pm->calculations_use_input_precision = 1;
John Bowler9994f252011-05-15 18:52:39 -05007729 perform_gamma_transform_tests(pm);
John Bowler5441e182011-05-18 18:57:12 -05007730 if (!calculations_use_input_precision)
7731 pm->calculations_use_input_precision = 0;
Glenn Randers-Pehrsonf1cf9022010-12-07 14:40:33 -06007732
John Bowlerb54498e2010-12-08 16:26:21 -06007733 if (summary)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007734 {
John Bowlerb54498e2010-12-08 16:26:21 -06007735 printf("Gamma correction error summary\n\n");
7736 printf("The printed value is the maximum error in the pixel values\n");
7737 printf("calculated by the libpng gamma correction code. The error\n");
7738 printf("is calculated as the difference between the output pixel\n");
7739 printf("value (always an integer) and the ideal value from the\n");
7740 printf("libpng specification (typically not an integer).\n\n");
7741
7742 printf("Expect this value to be less than .5 for 8 bit formats,\n");
7743 printf("less than 1 for formats with fewer than 8 bits and a small\n");
7744 printf("number (typically less than 5) for the 16 bit formats.\n");
7745 printf("For performance reasons the value for 16 bit formats\n");
7746 printf("increases when the image file includes an sBIT chunk.\n\n");
7747
John Bowlerd273ad22011-05-07 21:00:28 -05007748 summarize_gamma_errors(pm, 0/*who*/, 1);
John Bowlerb54498e2010-12-08 16:26:21 -06007749 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007750 }
7751
John Bowlerb54498e2010-12-08 16:26:21 -06007752 /* The sbit tests produce much larger errors: */
John Bowlerf21a0d02011-01-23 23:55:19 -06007753 if (pm->test_gamma_sbit)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007754 {
John Bowlerd273ad22011-05-07 21:00:28 -05007755 init_gamma_errors(pm);
John Bowler9994f252011-05-15 18:52:39 -05007756 perform_gamma_sbit_tests(pm);
John Bowlerb54498e2010-12-08 16:26:21 -06007757
7758 if (summary)
John Bowlerd273ad22011-05-07 21:00:28 -05007759 summarize_gamma_errors(pm, "sBIT", pm->sbitlow < 8U);
John Bowlerb54498e2010-12-08 16:26:21 -06007760 }
7761
John Bowler9994f252011-05-15 18:52:39 -05007762#ifdef DO_16BIT /* Should be READ_16BIT_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06007763 if (pm->test_gamma_strip16)
John Bowlerb54498e2010-12-08 16:26:21 -06007764 {
7765 /* The 16 to 8 bit strip operations: */
John Bowlerd273ad22011-05-07 21:00:28 -05007766 init_gamma_errors(pm);
John Bowler9994f252011-05-15 18:52:39 -05007767 perform_gamma_strip16_tests(pm);
John Bowlerb54498e2010-12-08 16:26:21 -06007768
7769 if (summary)
7770 {
7771 printf("Gamma correction with 16 to 8 bit reduction:\n");
7772 printf(" 16 bit gray: %.5f\n", pm->error_gray_16);
7773 printf(" 16 bit color: %.5f\n", pm->error_color_16);
7774 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007775 }
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05007776#endif
John Bowlerd273ad22011-05-07 21:00:28 -05007777
7778#ifdef PNG_READ_BACKGROUND_SUPPORTED
7779 if (pm->test_gamma_background)
7780 {
7781 init_gamma_errors(pm);
7782
John Bowler6f55ee22011-06-11 07:28:06 -05007783 /*TODO: remove this. Necessary because the current libpng
John Bowler5441e182011-05-18 18:57:12 -05007784 * implementation works in 8 bits:
7785 */
7786 if (pm->test_gamma_expand16)
7787 {
7788 pm->calculations_use_input_precision = 1;
7789 pm->maxout8 = .499; /* because the 16 bit background is smashed */
7790 }
John Bowler9994f252011-05-15 18:52:39 -05007791 perform_gamma_composition_tests(pm, PNG_BACKGROUND_GAMMA_UNIQUE,
John Bowlerd273ad22011-05-07 21:00:28 -05007792 pm->test_gamma_expand16);
John Bowler5441e182011-05-18 18:57:12 -05007793 if (!calculations_use_input_precision)
7794 pm->calculations_use_input_precision = 0;
7795 pm->maxout8 = maxout8;
John Bowlerd273ad22011-05-07 21:00:28 -05007796
7797 if (summary)
7798 summarize_gamma_errors(pm, "background", 1);
7799 }
7800#endif
7801
7802#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
7803 if (pm->test_gamma_alpha_mode)
7804 {
7805 int do_background;
7806
7807 init_gamma_errors(pm);
7808
John Bowler6f55ee22011-06-11 07:28:06 -05007809 /*TODO: remove this. Necessary because the current libpng
John Bowler5441e182011-05-18 18:57:12 -05007810 * implementation works in 8 bits:
7811 */
7812 if (pm->test_gamma_expand16)
7813 pm->calculations_use_input_precision = 1;
John Bowlerd273ad22011-05-07 21:00:28 -05007814 for (do_background = ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD;
7815 do_background <= ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN && !fail(pm);
7816 ++do_background)
John Bowler9994f252011-05-15 18:52:39 -05007817 perform_gamma_composition_tests(pm, do_background,
John Bowlerd273ad22011-05-07 21:00:28 -05007818 pm->test_gamma_expand16);
John Bowler5441e182011-05-18 18:57:12 -05007819 if (!calculations_use_input_precision)
7820 pm->calculations_use_input_precision = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05007821
7822 if (summary)
7823 summarize_gamma_errors(pm, "alpha mode", 1);
7824 }
7825#endif
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007826}
John Bowler4a12f4a2011-04-17 18:34:22 -05007827#endif /* PNG_READ_GAMMA_SUPPORTED */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05007828
John Bowler660c6e42010-12-19 06:22:23 -06007829/* INTERLACE MACRO VALIDATION */
7830/* This is copied verbatim from the specification, it is simply the pass
7831 * number in which each pixel in each 8x8 tile appears. The array must
7832 * be indexed adam7[y][x] and notice that the pass numbers are based at
7833 * 1, not 0 - the base libpng uses.
7834 */
7835static PNG_CONST
7836png_byte adam7[8][8] =
7837{
7838 { 1,6,4,6,2,6,4,6 },
7839 { 7,7,7,7,7,7,7,7 },
7840 { 5,6,5,6,5,6,5,6 },
7841 { 7,7,7,7,7,7,7,7 },
7842 { 3,6,4,6,3,6,4,6 },
7843 { 7,7,7,7,7,7,7,7 },
7844 { 5,6,5,6,5,6,5,6 },
7845 { 7,7,7,7,7,7,7,7 }
7846};
7847
7848/* This routine validates all the interlace support macros in png.h for
7849 * a variety of valid PNG widths and heights. It uses a number of similarly
7850 * named internal routines that feed off the above array.
7851 */
7852static png_uint_32
7853png_pass_start_row(int pass)
7854{
7855 int x, y;
7856 ++pass;
7857 for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
7858 return y;
7859 return 0xf;
7860}
7861
7862static png_uint_32
7863png_pass_start_col(int pass)
7864{
7865 int x, y;
7866 ++pass;
7867 for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
7868 return x;
7869 return 0xf;
7870}
7871
7872static int
7873png_pass_row_shift(int pass)
7874{
7875 int x, y, base=(-1), inc=8;
7876 ++pass;
7877 for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
7878 {
7879 if (base == (-1))
7880 base = y;
7881 else if (base == y)
7882 {}
7883 else if (inc == y-base)
7884 base=y;
7885 else if (inc == 8)
7886 inc = y-base, base=y;
7887 else if (inc != y-base)
7888 return 0xff; /* error - more than one 'inc' value! */
7889 }
7890
7891 if (base == (-1)) return 0xfe; /* error - no row in pass! */
7892
7893 /* The shift is always 1, 2 or 3 - no pass has all the rows! */
7894 switch (inc)
7895 {
7896case 2: return 1;
7897case 4: return 2;
7898case 8: return 3;
7899default: break;
7900 }
7901
7902 /* error - unrecognized 'inc' */
7903 return (inc << 8) + 0xfd;
7904}
7905
7906static int
7907png_pass_col_shift(int pass)
7908{
7909 int x, y, base=(-1), inc=8;
7910 ++pass;
7911 for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
7912 {
7913 if (base == (-1))
7914 base = x;
7915 else if (base == x)
7916 {}
7917 else if (inc == x-base)
7918 base=x;
7919 else if (inc == 8)
7920 inc = x-base, base=x;
7921 else if (inc != x-base)
7922 return 0xff; /* error - more than one 'inc' value! */
7923 }
7924
7925 if (base == (-1)) return 0xfe; /* error - no row in pass! */
7926
7927 /* The shift is always 1, 2 or 3 - no pass has all the rows! */
7928 switch (inc)
7929 {
7930case 1: return 0; /* pass 7 has all the columns */
7931case 2: return 1;
7932case 4: return 2;
7933case 8: return 3;
7934default: break;
7935 }
7936
7937 /* error - unrecognized 'inc' */
7938 return (inc << 8) + 0xfd;
7939}
7940
7941static png_uint_32
7942png_row_from_pass_row(png_uint_32 yIn, int pass)
7943{
7944 /* By examination of the array: */
7945 switch (pass)
7946 {
7947case 0: return yIn * 8;
7948case 1: return yIn * 8;
7949case 2: return yIn * 8 + 4;
7950case 3: return yIn * 4;
7951case 4: return yIn * 4 + 2;
7952case 5: return yIn * 2;
7953case 6: return yIn * 2 + 1;
7954default: break;
7955 }
7956
7957 return 0xff; /* bad pass number */
7958}
7959
7960static png_uint_32
7961png_col_from_pass_col(png_uint_32 xIn, int pass)
7962{
7963 /* By examination of the array: */
7964 switch (pass)
7965 {
7966case 0: return xIn * 8;
7967case 1: return xIn * 8 + 4;
7968case 2: return xIn * 4;
7969case 3: return xIn * 4 + 2;
7970case 4: return xIn * 2;
7971case 5: return xIn * 2 + 1;
7972case 6: return xIn;
7973default: break;
7974 }
7975
7976 return 0xff; /* bad pass number */
7977}
7978
7979static int
7980png_row_in_interlace_pass(png_uint_32 y, int pass)
7981{
7982 /* Is row 'y' in pass 'pass'? */
7983 int x;
7984 y &= 7;
7985 ++pass;
7986 for (x=0; x<8; ++x) if (adam7[y][x] == pass)
7987 return 1;
7988
7989 return 0;
7990}
7991
7992static int
7993png_col_in_interlace_pass(png_uint_32 x, int pass)
7994{
7995 /* Is column 'x' in pass 'pass'? */
7996 int y;
7997 x &= 7;
7998 ++pass;
7999 for (y=0; y<8; ++y) if (adam7[y][x] == pass)
8000 return 1;
8001
8002 return 0;
8003}
8004
8005static png_uint_32
8006png_pass_rows(png_uint_32 height, int pass)
8007{
8008 png_uint_32 tiles = height>>3;
8009 png_uint_32 rows = 0;
8010 unsigned int x, y;
8011
8012 height &= 7;
8013 ++pass;
8014 for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
8015 {
8016 rows += tiles;
8017 if (y < height) ++rows;
8018 break; /* i.e. break the 'x', column, loop. */
8019 }
8020
8021 return rows;
8022}
8023
8024static png_uint_32
8025png_pass_cols(png_uint_32 width, int pass)
8026{
8027 png_uint_32 tiles = width>>3;
8028 png_uint_32 cols = 0;
8029 unsigned int x, y;
8030
8031 width &= 7;
8032 ++pass;
8033 for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
8034 {
8035 cols += tiles;
8036 if (x < width) ++cols;
8037 break; /* i.e. break the 'y', row, loop. */
8038 }
8039
8040 return cols;
8041}
8042
8043static void
8044perform_interlace_macro_validation(void)
8045{
8046 /* The macros to validate, first those that depend only on pass:
8047 *
8048 * PNG_PASS_START_ROW(pass)
8049 * PNG_PASS_START_COL(pass)
8050 * PNG_PASS_ROW_SHIFT(pass)
8051 * PNG_PASS_COL_SHIFT(pass)
8052 */
8053 int pass;
8054
8055 for (pass=0; pass<7; ++pass)
8056 {
8057 png_uint_32 m, f, v;
8058
8059 m = PNG_PASS_START_ROW(pass);
8060 f = png_pass_start_row(pass);
8061 if (m != f)
8062 {
8063 fprintf(stderr, "PNG_PASS_START_ROW(%d) = %u != %x\n", pass, m, f);
8064 exit(1);
8065 }
8066
8067 m = PNG_PASS_START_COL(pass);
8068 f = png_pass_start_col(pass);
8069 if (m != f)
8070 {
8071 fprintf(stderr, "PNG_PASS_START_COL(%d) = %u != %x\n", pass, m, f);
8072 exit(1);
8073 }
8074
8075 m = PNG_PASS_ROW_SHIFT(pass);
8076 f = png_pass_row_shift(pass);
8077 if (m != f)
8078 {
8079 fprintf(stderr, "PNG_PASS_ROW_SHIFT(%d) = %u != %x\n", pass, m, f);
8080 exit(1);
8081 }
8082
8083 m = PNG_PASS_COL_SHIFT(pass);
8084 f = png_pass_col_shift(pass);
8085 if (m != f)
8086 {
8087 fprintf(stderr, "PNG_PASS_COL_SHIFT(%d) = %u != %x\n", pass, m, f);
8088 exit(1);
8089 }
8090
8091 /* Macros that depend on the image or sub-image height too:
8092 *
8093 * PNG_PASS_ROWS(height, pass)
8094 * PNG_PASS_COLS(width, pass)
8095 * PNG_ROW_FROM_PASS_ROW(yIn, pass)
8096 * PNG_COL_FROM_PASS_COL(xIn, pass)
8097 * PNG_ROW_IN_INTERLACE_PASS(y, pass)
8098 * PNG_COL_IN_INTERLACE_PASS(x, pass)
8099 */
8100 for (v=0;;)
8101 {
8102 /* First the base 0 stuff: */
8103 m = PNG_ROW_FROM_PASS_ROW(v, pass);
8104 f = png_row_from_pass_row(v, pass);
8105 if (m != f)
8106 {
8107 fprintf(stderr, "PNG_ROW_FROM_PASS_ROW(%u, %d) = %u != %x\n",
8108 v, pass, m, f);
8109 exit(1);
8110 }
8111
8112 m = PNG_COL_FROM_PASS_COL(v, pass);
8113 f = png_col_from_pass_col(v, pass);
8114 if (m != f)
8115 {
8116 fprintf(stderr, "PNG_COL_FROM_PASS_COL(%u, %d) = %u != %x\n",
8117 v, pass, m, f);
8118 exit(1);
8119 }
8120
8121 m = PNG_ROW_IN_INTERLACE_PASS(v, pass);
8122 f = png_row_in_interlace_pass(v, pass);
8123 if (m != f)
8124 {
8125 fprintf(stderr, "PNG_ROW_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
8126 v, pass, m, f);
8127 exit(1);
8128 }
8129
8130 m = PNG_COL_IN_INTERLACE_PASS(v, pass);
8131 f = png_col_in_interlace_pass(v, pass);
8132 if (m != f)
8133 {
8134 fprintf(stderr, "PNG_COL_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
8135 v, pass, m, f);
8136 exit(1);
8137 }
8138
8139 /* Then the base 1 stuff: */
8140 ++v;
8141 m = PNG_PASS_ROWS(v, pass);
8142 f = png_pass_rows(v, pass);
8143 if (m != f)
8144 {
8145 fprintf(stderr, "PNG_PASS_ROWS(%u, %d) = %u != %x\n",
8146 v, pass, m, f);
8147 exit(1);
8148 }
8149
8150 m = PNG_PASS_COLS(v, pass);
8151 f = png_pass_cols(v, pass);
8152 if (m != f)
8153 {
8154 fprintf(stderr, "PNG_PASS_COLS(%u, %d) = %u != %x\n",
8155 v, pass, m, f);
8156 exit(1);
8157 }
8158
8159 /* Move to the next v - the stepping algorithm starts skipping
8160 * values above 1024.
8161 */
8162 if (v > 1024)
8163 {
8164 if (v == PNG_UINT_31_MAX)
8165 break;
8166
8167 v = (v << 1) ^ v;
8168 if (v >= PNG_UINT_31_MAX)
8169 v = PNG_UINT_31_MAX-1;
8170 }
8171 }
8172 }
8173}
8174
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008175/* main program */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05008176int main(int argc, PNG_CONST char **argv)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008177{
John Bowler660c6e42010-12-19 06:22:23 -06008178 volatile int summary = 1; /* Print the error summary at the end */
John Bowler1921e6d2011-05-16 20:57:54 -05008179 volatile int memstats = 0; /* Print memory statistics at the end */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008180
8181 /* Create the given output file on success: */
8182 PNG_CONST char *volatile touch = NULL;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008183
8184 /* This is an array of standard gamma values (believe it or not I've seen
8185 * every one of these mentioned somewhere.)
8186 *
8187 * In the following list the most useful values are first!
8188 */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008189 static double
8190 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 -05008191
John Bowler1921e6d2011-05-16 20:57:54 -05008192 /* This records the command and arguments: */
8193 size_t cp = 0;
8194 char command[1024];
8195
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008196 png_modifier pm;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008197 context(&pm.this, fault);
8198
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008199 modifier_init(&pm);
8200
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05008201 /* Preallocate the image buffer, because we know how big it needs to be,
John Bowler9994f252011-05-15 18:52:39 -05008202 * note that, for testing purposes, it is deliberately mis-aligned by tag
8203 * bytes either side. All rows have an additional five bytes of padding for
8204 * overwrite checking.
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05008205 */
John Bowler9994f252011-05-15 18:52:39 -05008206 store_ensure_image(&pm.this, NULL, 2, TRANSFORM_ROWMAX, TRANSFORM_HEIGHTMAX);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05008207
John Bowler1921e6d2011-05-16 20:57:54 -05008208 /* Don't give argv[0], it's normally some horrible libtool string: */
8209 cp = safecat(command, sizeof command, cp, "pngvalid");
8210
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008211 /* Default to error on warning: */
8212 pm.this.treat_warnings_as_errors = 1;
8213
8214 /* Store the test gammas */
8215 pm.gammas = gammas;
John Bowlerafea7d12011-01-28 06:38:14 -06008216 pm.ngammas = 0; /* default to off */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008217 pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */
John Bowler5441e182011-05-18 18:57:12 -05008218 /* The following allows results to pass if they correspond to anything in the
8219 * transformed range [input-.5,input+.5]; this is is required because of the
8220 * way libpng treates the 16_TO_8 flag when building the gamma tables.
8221 *
8222 * TODO: review this
8223 */
8224 pm.use_input_precision_16to8 = 1U;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008225
Glenn Randers-Pehrsonf1cf9022010-12-07 14:40:33 -06008226 /* Some default values (set the behavior for 'make check' here).
8227 * These values simply control the maximum error permitted in the gamma
8228 * transformations. The practial limits for human perception are described
8229 * below (the setting for maxpc16), however for 8 bit encodings it isn't
8230 * possible to meet the accepted capabilities of human vision - i.e. 8 bit
Glenn Randers-Pehrson9f044c12010-12-07 14:59:43 -06008231 * images can never be good enough, regardless of encoding.
Glenn Randers-Pehrsonf1cf9022010-12-07 14:40:33 -06008232 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008233 pm.maxout8 = .1; /* Arithmetic error in *encoded* value */
8234 pm.maxabs8 = .00005; /* 1/20000 */
John Bowler5441e182011-05-18 18:57:12 -05008235 pm.maxcalc8 = .004; /* +/-1 in 8 bits for compose errors */
John Bowlerb54498e2010-12-08 16:26:21 -06008236 pm.maxpc8 = .499; /* I.e., .499% fractional error */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008237 pm.maxout16 = .499; /* Error in *encoded* value */
8238 pm.maxabs16 = .00005;/* 1/20000 */
John Bowler5441e182011-05-18 18:57:12 -05008239 pm.maxcalc16 =.000015;/* +/-1 in 16 bits for compose errors */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06008240
8241 /* NOTE: this is a reasonable perceptual limit. We assume that humans can
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008242 * perceive light level differences of 1% over a 100:1 range, so we need to
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06008243 * maintain 1 in 10000 accuracy (in linear light space), which is what the
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008244 * following guarantees. It also allows significantly higher errors at
8245 * higher 16 bit values, which is important for performance. The actual
8246 * maximum 16 bit error is about +/-1.9 in the fixed point implementation but
8247 * this is only allowed for values >38149 by the following:
8248 */
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06008249 pm.maxpc16 = .005; /* I.e., 1/200% - 1/20000 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008250
8251 /* Now parse the command line options. */
8252 while (--argc >= 1)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008253 {
John Bowler5441e182011-05-18 18:57:12 -05008254 int catmore = 0; /* Set if the argument has an argument. */
8255
John Bowler1921e6d2011-05-16 20:57:54 -05008256 /* Record each argument for posterity: */
8257 cp = safecat(command, sizeof command, cp, " ");
8258 cp = safecat(command, sizeof command, cp, *++argv);
8259
8260 if (strcmp(*argv, "-v") == 0)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008261 pm.this.verbose = 1;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008262
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008263 else if (strcmp(*argv, "-l") == 0)
8264 pm.log = 1;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008265
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008266 else if (strcmp(*argv, "-q") == 0)
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05008267 summary = pm.this.verbose = pm.log = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008268
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008269 else if (strcmp(*argv, "-w") == 0)
8270 pm.this.treat_warnings_as_errors = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008271
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05008272 else if (strcmp(*argv, "--speed") == 0)
John Bowlerb54498e2010-12-08 16:26:21 -06008273 pm.this.speed = 1, pm.ngammas = (sizeof gammas)/(sizeof gammas[0]),
John Bowler1921e6d2011-05-16 20:57:54 -05008274 pm.test_standard = 0, summary = 0;
8275
8276 else if (strcmp(*argv, "--memory") == 0)
8277 memstats = 1;
John Bowlerb54498e2010-12-08 16:26:21 -06008278
John Bowler660c6e42010-12-19 06:22:23 -06008279 else if (strcmp(*argv, "--size") == 0)
8280 pm.test_size = 1;
8281
John Bowlerafea7d12011-01-28 06:38:14 -06008282 else if (strcmp(*argv, "--nosize") == 0)
8283 pm.test_size = 0;
8284
8285 else if (strcmp(*argv, "--standard") == 0)
8286 pm.test_standard = 1;
8287
John Bowlerb54498e2010-12-08 16:26:21 -06008288 else if (strcmp(*argv, "--nostandard") == 0)
8289 pm.test_standard = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008290
John Bowlerafea7d12011-01-28 06:38:14 -06008291 else if (strcmp(*argv, "--transform") == 0)
8292 pm.test_transform = 1;
8293
John Bowlerf21a0d02011-01-23 23:55:19 -06008294 else if (strcmp(*argv, "--notransform") == 0)
8295 pm.test_transform = 0;
8296
John Bowler4a12f4a2011-04-17 18:34:22 -05008297#ifdef PNG_READ_TRANSFORMS_SUPPORTED
John Bowlerafea7d12011-01-28 06:38:14 -06008298 else if (strncmp(*argv, "--transform-disable=",
8299 sizeof "--transform-disable") == 0)
8300 {
8301 pm.test_transform = 1;
8302 transform_disable(*argv + sizeof "--transform-disable");
8303 }
8304
8305 else if (strncmp(*argv, "--transform-enable=",
8306 sizeof "--transform-enable") == 0)
8307 {
8308 pm.test_transform = 1;
8309 transform_enable(*argv + sizeof "--transform-enable");
8310 }
John Bowler4a12f4a2011-04-17 18:34:22 -05008311#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
John Bowlerafea7d12011-01-28 06:38:14 -06008312
8313 else if (strcmp(*argv, "--gamma") == 0)
8314 {
8315 /* Just do two gamma tests here (2.2 and linear) for speed: */
8316 pm.ngammas = 2U;
8317 pm.test_gamma_threshold = 1;
8318 pm.test_gamma_transform = 1;
8319 pm.test_gamma_sbit = 1;
8320 pm.test_gamma_strip16 = 1;
John Bowlerd273ad22011-05-07 21:00:28 -05008321 pm.test_gamma_background = 1;
8322 pm.test_gamma_alpha_mode = 1;
John Bowlerafea7d12011-01-28 06:38:14 -06008323 }
8324
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05008325 else if (strcmp(*argv, "--nogamma") == 0)
8326 pm.ngammas = 0;
8327
John Bowlerafea7d12011-01-28 06:38:14 -06008328 else if (strcmp(*argv, "--gamma-threshold") == 0)
8329 pm.ngammas = 2U, pm.test_gamma_threshold = 1;
8330
John Bowlerb54498e2010-12-08 16:26:21 -06008331 else if (strcmp(*argv, "--nogamma-threshold") == 0)
John Bowlerf21a0d02011-01-23 23:55:19 -06008332 pm.test_gamma_threshold = 0;
John Bowlerb54498e2010-12-08 16:26:21 -06008333
John Bowlerafea7d12011-01-28 06:38:14 -06008334 else if (strcmp(*argv, "--gamma-transform") == 0)
8335 pm.ngammas = 2U, pm.test_gamma_transform = 1;
8336
John Bowlerb54498e2010-12-08 16:26:21 -06008337 else if (strcmp(*argv, "--nogamma-transform") == 0)
John Bowlerf21a0d02011-01-23 23:55:19 -06008338 pm.test_gamma_transform = 0;
John Bowlerb54498e2010-12-08 16:26:21 -06008339
John Bowlerafea7d12011-01-28 06:38:14 -06008340 else if (strcmp(*argv, "--gamma-sbit") == 0)
8341 pm.ngammas = 2U, pm.test_gamma_sbit = 1;
8342
John Bowlerb54498e2010-12-08 16:26:21 -06008343 else if (strcmp(*argv, "--nogamma-sbit") == 0)
John Bowlerf21a0d02011-01-23 23:55:19 -06008344 pm.test_gamma_sbit = 0;
John Bowlerb54498e2010-12-08 16:26:21 -06008345
John Bowlerafea7d12011-01-28 06:38:14 -06008346 else if (strcmp(*argv, "--gamma-16-to-8") == 0)
8347 pm.ngammas = 2U, pm.test_gamma_strip16 = 1;
8348
John Bowlerb54498e2010-12-08 16:26:21 -06008349 else if (strcmp(*argv, "--nogamma-16-to-8") == 0)
John Bowlerf21a0d02011-01-23 23:55:19 -06008350 pm.test_gamma_strip16 = 0;
John Bowlerb54498e2010-12-08 16:26:21 -06008351
John Bowlerd273ad22011-05-07 21:00:28 -05008352 else if (strcmp(*argv, "--gamma-background") == 0)
8353 pm.ngammas = 2U, pm.test_gamma_background = 1;
8354
8355 else if (strcmp(*argv, "--nogamma-background") == 0)
8356 pm.test_gamma_background = 0;
8357
8358 else if (strcmp(*argv, "--gamma-alpha-mode") == 0)
8359 pm.ngammas = 2U, pm.test_gamma_alpha_mode = 1;
8360
8361 else if (strcmp(*argv, "--nogamma-alpha-mode") == 0)
8362 pm.test_gamma_alpha_mode = 0;
8363
8364 else if (strcmp(*argv, "--expand16") == 0)
8365 pm.test_gamma_expand16 = 1;
8366
8367 else if (strcmp(*argv, "--noexpand16") == 0)
8368 pm.test_gamma_expand16 = 0;
8369
8370 else if (strcmp(*argv, "--more-gammas") == 0)
8371 pm.ngammas = 3U;
8372
John Bowlerafea7d12011-01-28 06:38:14 -06008373 else if (strcmp(*argv, "--all-gammas") == 0)
8374 pm.ngammas = (sizeof gammas)/(sizeof gammas[0]);
8375
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05008376 else if (strcmp(*argv, "--progressive-read") == 0)
8377 pm.this.progressive = 1;
8378
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05008379 else if (strcmp(*argv, "--interlace") == 0)
8380 pm.interlace_type = PNG_INTERLACE_ADAM7;
8381
John Bowlerd273ad22011-05-07 21:00:28 -05008382 else if (strcmp(*argv, "--use-input-precision") == 0)
8383 pm.use_input_precision = 1;
8384
John Bowler5441e182011-05-18 18:57:12 -05008385 else if (strcmp(*argv, "--calculations-use-input-precision") == 0)
8386 pm.calculations_use_input_precision = 1;
John Bowlerd273ad22011-05-07 21:00:28 -05008387
John Bowler5441e182011-05-18 18:57:12 -05008388 else if (strcmp(*argv, "--assume-16-bit-calculations") == 0)
8389 pm.assume_16_bit_calculations = 1;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008390
John Bowler5441e182011-05-18 18:57:12 -05008391 else if (strcmp(*argv, "--calculations-follow-bit-depth") == 0)
8392 pm.calculations_use_input_precision =
8393 pm.assume_16_bit_calculations = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008394
John Bowler5441e182011-05-18 18:57:12 -05008395 else if (argc > 1 && strcmp(*argv, "--sbitlow") == 0)
8396 --argc, pm.sbitlow = (png_byte)atoi(*++argv), catmore = 1;
8397
8398 else if (argc > 1 && strcmp(*argv, "--touch") == 0)
8399 --argc, touch = *++argv, catmore = 1;
8400
8401 else if (argc > 1 && strncmp(*argv, "--max", 5) == 0)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008402 {
8403 --argc;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008404
John Bowlerd273ad22011-05-07 21:00:28 -05008405 if (strcmp(5+*argv, "abs8") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008406 pm.maxabs8 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008407
John Bowlerd273ad22011-05-07 21:00:28 -05008408 else if (strcmp(5+*argv, "abs16") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008409 pm.maxabs16 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008410
John Bowlerd273ad22011-05-07 21:00:28 -05008411 else if (strcmp(5+*argv, "calc8") == 0)
8412 pm.maxcalc8 = atof(*++argv);
8413
8414 else if (strcmp(5+*argv, "calc16") == 0)
8415 pm.maxcalc16 = atof(*++argv);
8416
8417 else if (strcmp(5+*argv, "out8") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008418 pm.maxout8 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008419
John Bowlerd273ad22011-05-07 21:00:28 -05008420 else if (strcmp(5+*argv, "out16") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008421 pm.maxout16 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008422
John Bowlerd273ad22011-05-07 21:00:28 -05008423 else if (strcmp(5+*argv, "pc8") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008424 pm.maxpc8 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008425
John Bowlerd273ad22011-05-07 21:00:28 -05008426 else if (strcmp(5+*argv, "pc16") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008427 pm.maxpc16 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008428
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008429 else
8430 {
8431 fprintf(stderr, "pngvalid: %s: unknown 'max' option\n", *argv);
8432 exit(1);
8433 }
John Bowler5441e182011-05-18 18:57:12 -05008434
8435 catmore = 1;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008436 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008437
John Bowler6f55ee22011-06-11 07:28:06 -05008438 else if (strcmp(*argv, "--log8") == 0)
8439 --argc, pm.log8 = atof(*++argv), catmore = 1;
8440
8441 else if (strcmp(*argv, "--log16") == 0)
8442 --argc, pm.log16 = atof(*++argv), catmore = 1;
8443
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008444 else
8445 {
8446 fprintf(stderr, "pngvalid: %s: unknown argument\n", *argv);
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05008447 exit(1);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008448 }
John Bowler5441e182011-05-18 18:57:12 -05008449
8450 if (catmore) /* consumed an extra *argv */
8451 {
8452 cp = safecat(command, sizeof command, cp, " ");
8453 cp = safecat(command, sizeof command, cp, *argv);
8454 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008455 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008456
John Bowlerafea7d12011-01-28 06:38:14 -06008457 /* If pngvalid is run with no arguments default to a reasonable set of the
8458 * tests.
8459 */
8460 if (pm.test_standard == 0 && pm.test_size == 0 && pm.test_transform == 0 &&
8461 pm.ngammas == 0)
8462 {
John Bowlerd273ad22011-05-07 21:00:28 -05008463 /* Make this do all the tests done in the test shell scripts with the same
8464 * parameters, where possible. The limitation is that all the progressive
8465 * read and interlace stuff has to be done in separate runs, so only the
8466 * basic 'standard' and 'size' tests are done.
8467 */
John Bowlerafea7d12011-01-28 06:38:14 -06008468 pm.test_standard = 1;
8469 pm.test_size = 1;
8470 pm.test_transform = 1;
John Bowlerd273ad22011-05-07 21:00:28 -05008471 pm.ngammas = 2U;
John Bowlerafea7d12011-01-28 06:38:14 -06008472 }
8473
8474 if (pm.ngammas > 0 &&
8475 pm.test_gamma_threshold == 0 && pm.test_gamma_transform == 0 &&
John Bowlerd273ad22011-05-07 21:00:28 -05008476 pm.test_gamma_sbit == 0 && pm.test_gamma_strip16 == 0 &&
8477 pm.test_gamma_background == 0 && pm.test_gamma_alpha_mode == 0)
John Bowlerafea7d12011-01-28 06:38:14 -06008478 {
8479 pm.test_gamma_threshold = 1;
8480 pm.test_gamma_transform = 1;
8481 pm.test_gamma_sbit = 1;
8482 pm.test_gamma_strip16 = 1;
John Bowlerd273ad22011-05-07 21:00:28 -05008483 pm.test_gamma_background = 1;
8484 pm.test_gamma_alpha_mode = 1;
John Bowlerafea7d12011-01-28 06:38:14 -06008485 }
Glenn Randers-Pehrsonc36bb792011-02-12 09:49:07 -06008486
John Bowlerafea7d12011-01-28 06:38:14 -06008487 else if (pm.ngammas == 0)
8488 {
8489 /* Nothing to test so turn everything off: */
8490 pm.test_gamma_threshold = 0;
8491 pm.test_gamma_transform = 0;
8492 pm.test_gamma_sbit = 0;
8493 pm.test_gamma_strip16 = 0;
John Bowlerd273ad22011-05-07 21:00:28 -05008494 pm.test_gamma_background = 0;
8495 pm.test_gamma_alpha_mode = 0;
John Bowlerafea7d12011-01-28 06:38:14 -06008496 }
8497
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008498 Try
8499 {
8500 /* Make useful base images */
John Bowler660c6e42010-12-19 06:22:23 -06008501 make_transform_images(&pm.this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008502
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008503 /* Perform the standard and gamma tests. */
John Bowlerb54498e2010-12-08 16:26:21 -06008504 if (pm.test_standard)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008505 {
John Bowler660c6e42010-12-19 06:22:23 -06008506 perform_interlace_macro_validation();
John Bowler88b77cc2011-05-05 06:49:55 -05008507 perform_formatting_test(&pm.this);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008508 perform_standard_test(&pm);
8509 perform_error_test(&pm);
8510 }
8511
John Bowler660c6e42010-12-19 06:22:23 -06008512 /* Various oddly sized images: */
8513 if (pm.test_size)
8514 {
8515 make_size_images(&pm.this);
8516 perform_size_test(&pm);
8517 }
8518
John Bowler4a12f4a2011-04-17 18:34:22 -05008519#ifdef PNG_READ_TRANSFORMS_SUPPORTED
John Bowlerf21a0d02011-01-23 23:55:19 -06008520 /* Combinatorial transforms: */
8521 if (pm.test_transform)
8522 perform_transform_test(&pm);
John Bowler4a12f4a2011-04-17 18:34:22 -05008523#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
John Bowlerf21a0d02011-01-23 23:55:19 -06008524
John Bowler4a12f4a2011-04-17 18:34:22 -05008525#ifdef PNG_READ_GAMMA_SUPPORTED
John Bowlerb54498e2010-12-08 16:26:21 -06008526 if (pm.ngammas > 0)
John Bowler1921e6d2011-05-16 20:57:54 -05008527 perform_gamma_test(&pm, summary);
John Bowler4a12f4a2011-04-17 18:34:22 -05008528#endif
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008529 }
8530
8531 Catch(fault)
8532 {
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05008533 fprintf(stderr, "pngvalid: test aborted (probably failed in cleanup)\n");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05008534 if (!pm.this.verbose)
8535 {
8536 if (pm.this.error[0] != 0)
8537 fprintf(stderr, "pngvalid: first error: %s\n", pm.this.error);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06008538
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05008539 fprintf(stderr, "pngvalid: run with -v to see what happened\n");
8540 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008541 exit(1);
8542 }
8543
John Bowler1921e6d2011-05-16 20:57:54 -05008544 if (summary)
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05008545 {
John Bowler5441e182011-05-18 18:57:12 -05008546 printf("%s: %s (%s point arithmetic)\n",
John Bowler1921e6d2011-05-16 20:57:54 -05008547 (pm.this.nerrors || (pm.this.treat_warnings_as_errors &&
8548 pm.this.nwarnings)) ? "FAIL" : "PASS",
8549 command,
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008550#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || PNG_LIBPNG_VER < 10500
John Bowler5441e182011-05-18 18:57:12 -05008551 "floating"
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008552#else
John Bowler5441e182011-05-18 18:57:12 -05008553 "fixed"
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008554#endif
John Bowler5441e182011-05-18 18:57:12 -05008555 );
John Bowler1921e6d2011-05-16 20:57:54 -05008556 }
8557
8558 if (memstats)
8559 {
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05008560 printf("Allocated memory statistics (in bytes):\n"
Glenn Randers-Pehrson9a75d992010-10-08 16:27:14 -05008561 "\tread %lu maximum single, %lu peak, %lu total\n"
8562 "\twrite %lu maximum single, %lu peak, %lu total\n",
8563 (unsigned long)pm.this.read_memory_pool.max_max,
8564 (unsigned long)pm.this.read_memory_pool.max_limit,
8565 (unsigned long)pm.this.read_memory_pool.max_total,
8566 (unsigned long)pm.this.write_memory_pool.max_max,
8567 (unsigned long)pm.this.write_memory_pool.max_limit,
8568 (unsigned long)pm.this.write_memory_pool.max_total);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05008569 }
8570
8571 /* Do this here to provoke memory corruption errors in memory not directly
8572 * allocated by libpng - not a complete test, but better than nothing.
8573 */
8574 store_delete(&pm.this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008575
8576 /* Error exit if there are any errors, and maybe if there are any
8577 * warnings.
8578 */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05008579 if (pm.this.nerrors || (pm.this.treat_warnings_as_errors &&
8580 pm.this.nwarnings))
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008581 {
8582 if (!pm.this.verbose)
8583 fprintf(stderr, "pngvalid: %s\n", pm.this.error);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008584
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008585 fprintf(stderr, "pngvalid: %d errors, %d warnings\n", pm.this.nerrors,
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008586 pm.this.nwarnings);
8587
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008588 exit(1);
8589 }
8590
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05008591 /* Success case. */
8592 if (touch != NULL)
8593 {
8594 FILE *fsuccess = fopen(touch, "wt");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008595
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05008596 if (fsuccess != NULL)
8597 {
8598 int error = 0;
8599 fprintf(fsuccess, "PNG validation succeeded\n");
8600 fflush(fsuccess);
8601 error = ferror(fsuccess);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05008602
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05008603 if (fclose(fsuccess) || error)
8604 {
8605 fprintf(stderr, "%s: write failed\n", touch);
8606 exit(1);
8607 }
8608 }
8609 }
8610
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05008611 return 0;
8612}