blob: ff013730ec7579b554af018948a605e6015038f8 [file] [log] [blame]
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001
2/* pngvalid.c - validate libpng by constructing then reading png files.
3 *
Glenn Randers-Pehrsonf5ea1b72011-01-06 06:42:51 -06004 * Last changed in libpng 1.5.0 [January 6, 2011]
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
22#include "png.h"
23#include "zlib.h" /* For crc32 */
24
25#include <stdlib.h> /* For malloc */
26#include <string.h> /* For memcpy, memset */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050027#include <math.h> /* For floor */
28
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -050029/* Unused formal parameter errors are removed using the following macro which is
30 * expected to have no bad effects on performance.
31 */
32#ifndef UNUSED
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -050033# define UNUSED(param) param = param;
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -050034#endif
35
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -050036/***************************** EXCEPTION HANDLING *****************************/
37#include "contrib/visupng/cexcept.h"
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -050038struct png_store;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -050039define_exception_type(struct png_store*);
40
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -050041/* The following are macros to reduce typing everywhere where the well known
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -050042 * name 'the_exception_context' must be defined.
43 */
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -050044#define anon_context(ps) struct exception_context *the_exception_context = \
45 &(ps)->exception_context
46#define context(ps,fault) anon_context(ps); png_store *fault
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -050047
John Bowler660c6e42010-12-19 06:22:23 -060048/******************************* UTILITIES ************************************/
49/* Error handling is particularly problematic in production code - error
50 * handlers often themselves have bugs which lead to programs that detect
51 * minor errors crashing. The following functions deal with one very
52 * common class of errors in error handlers - attempting to format error or
53 * warning messages into buffers that are too small.
54 */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -050055static size_t safecat(char *buffer, size_t bufsize, size_t pos,
56 PNG_CONST char *cat)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050057{
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -050058 while (pos < bufsize && cat != NULL && *cat != 0)
59 buffer[pos++] = *cat++;
60
61 if (pos >= bufsize)
62 pos = bufsize-1;
63
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050064 buffer[pos] = 0;
65 return pos;
66}
67
68static size_t safecatn(char *buffer, size_t bufsize, size_t pos, int n)
69{
70 char number[64];
71 sprintf(number, "%d", n);
72 return safecat(buffer, bufsize, pos, number);
73}
74
75static size_t safecatd(char *buffer, size_t bufsize, size_t pos, double d,
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -050076 int precision)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050077{
78 char number[64];
79 sprintf(number, "%.*f", precision, d);
80 return safecat(buffer, bufsize, pos, number);
81}
82
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -050083static PNG_CONST char invalid[] = "invalid";
84static PNG_CONST char sep[] = ": ";
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050085
86/* NOTE: this is indexed by ln2(bit_depth)! */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -050087static PNG_CONST char *bit_depths[8] =
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050088{
89 "1", "2", "4", "8", "16", invalid, invalid, invalid
90};
91
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -050092static PNG_CONST char *colour_types[8] =
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050093{
94 "greyscale", invalid, "truecolour", "indexed-colour",
95 "greyscale with alpha", invalid, "truecolour with alpha", invalid
96};
97
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -050098/* To get log-bit-depth from bit depth, returns 0 to 7 (7 on error). */
99static unsigned int
100log2depth(png_byte bit_depth)
101{
102 switch (bit_depth)
103 {
104 case 1:
105 return 0;
106
107 case 2:
108 return 1;
109
110 case 4:
111 return 2;
112
113 case 8:
114 return 3;
115
116 case 16:
117 return 4;
118
119 default:
120 return 7;
121 }
122}
123
John Bowler660c6e42010-12-19 06:22:23 -0600124/* A numeric ID based on PNG file characteristics. The 'do_interlace' field
125 * simply records whether pngvalid did the interlace itself or whether it
126 * was done by libpng. Width and height must be less than 256.
127 */
128#define FILEID(col, depth, interlace, width, height, do_interlace) \
129 ((png_uint_32)((col) + ((depth)<<3) + ((interlace)<<8) + \
130 (((do_interlace)!=0)<<15) + ((width)<<16) + ((height)<<24)))
131
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500132#define COL_FROM_ID(id) ((png_byte)((id)& 0x7U))
133#define DEPTH_FROM_ID(id) ((png_byte)(((id) >> 3) & 0x1fU))
John Bowler660c6e42010-12-19 06:22:23 -0600134#define INTERLACE_FROM_ID(id) ((int)(((id) >> 8) & 0x3))
135#define DO_INTERLACE_FROM_ID(id) ((int)(((id)>>15) & 1))
136#define WIDTH_FROM_ID(id) (((id)>>16) & 0xff)
137#define HEIGHT_FROM_ID(id) (((id)>>24) & 0xff)
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500138
139/* Utility to construct a standard name for a standard image. */
140static size_t
141standard_name(char *buffer, size_t bufsize, size_t pos, png_byte colour_type,
John Bowler660c6e42010-12-19 06:22:23 -0600142 int log_bit_depth, int interlace_type, png_uint_32 w, png_uint_32 h,
143 int do_interlace)
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500144{
145 pos = safecat(buffer, bufsize, pos, colour_types[colour_type]);
146 pos = safecat(buffer, bufsize, pos, " ");
147 pos = safecat(buffer, bufsize, pos, bit_depths[log_bit_depth]);
John Bowler660c6e42010-12-19 06:22:23 -0600148 pos = safecat(buffer, bufsize, pos, " bit ");
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500149
150 if (interlace_type != PNG_INTERLACE_NONE)
John Bowler660c6e42010-12-19 06:22:23 -0600151 pos = safecat(buffer, bufsize, pos, "interlaced");
152 if (do_interlace)
153 pos = safecat(buffer, bufsize, pos, "(pngvalid)");
154 else
155 pos = safecat(buffer, bufsize, pos, "(libpng)");
156 if (w > 0 || h > 0)
157 {
158 pos = safecat(buffer, bufsize, pos, " ");
159 pos = safecatn(buffer, bufsize, pos, w);
160 pos = safecat(buffer, bufsize, pos, "x");
161 pos = safecatn(buffer, bufsize, pos, h);
162 }
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500163
164 return pos;
165}
166
167static size_t
168standard_name_from_id(char *buffer, size_t bufsize, size_t pos, png_uint_32 id)
169{
170 return standard_name(buffer, bufsize, pos, COL_FROM_ID(id),
John Bowler660c6e42010-12-19 06:22:23 -0600171 log2depth(DEPTH_FROM_ID(id)), INTERLACE_FROM_ID(id),
172 WIDTH_FROM_ID(id), HEIGHT_FROM_ID(id), DO_INTERLACE_FROM_ID(id));
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500173}
174
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -0500175/* Convenience API and defines to list valid formats. Note that 16 bit read and
176 * write support is required to do 16 bit read tests (we must be able to make a
177 * 16 bit image to test!)
178 */
179#ifdef PNG_WRITE_16BIT_SUPPORTED
180# define WRITE_BDHI 4
181# ifdef PNG_READ_16BIT_SUPPORTED
182# define READ_BDHI 4
183# define DO_16BIT
184# endif
185#else
186# define WRITE_BDHI 3
187#endif
188#ifndef DO_16BIT
189# define READ_BDHI 3
190#endif
191
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500192static int
193next_format(png_bytep colour_type, png_bytep bit_depth)
194{
195 if (*bit_depth == 0)
196 {
197 *colour_type = 0, *bit_depth = 1;
198 return 1;
199 }
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -0500200
201 *bit_depth = (png_byte)(*bit_depth << 1);
202
203 /* Palette images are restricted to 8 bit depth */
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -0500204 if (*bit_depth <= 8
205# ifdef DO_16BIT
206 || (*colour_type != 3 && *bit_depth <= 16)
207# endif
208 )
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -0500209 return 1;
210
211 /* Move to the next color type, or return 0 at the end. */
212 switch (*colour_type)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500213 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500214 case 0:
215 *colour_type = 2;
216 *bit_depth = 8;
217 return 1;
218
219 case 2:
220 *colour_type = 3;
221 *bit_depth = 1;
222 return 1;
223
224 case 3:
225 *colour_type = 4;
226 *bit_depth = 8;
227 return 1;
228
229 case 4:
230 *colour_type = 6;
231 *bit_depth = 8;
232 return 1;
233
234 default:
235 return 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500236 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500237}
238
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -0500239static unsigned int
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500240sample(png_const_bytep row, png_byte colour_type, png_byte bit_depth,
John Bowler168a4332011-01-16 19:32:22 -0600241 png_uint_32 x, unsigned int sample_index)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500242{
John Bowler168a4332011-01-16 19:32:22 -0600243 png_uint_32 bit_index, result;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -0600244
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500245 /* Find a sample index for the desired sample: */
246 x *= bit_depth;
John Bowler168a4332011-01-16 19:32:22 -0600247 bit_index = x;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500248
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500249 if ((colour_type & 1) == 0) /* !palette */
250 {
251 if (colour_type & 2)
John Bowler168a4332011-01-16 19:32:22 -0600252 bit_index *= 3;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500253
254 if (colour_type & 4)
John Bowler168a4332011-01-16 19:32:22 -0600255 bit_index += x; /* Alpha channel */
John Bowlere2062f92011-01-15 22:36:33 -0600256
257 if (colour_type & (2+4))
John Bowler168a4332011-01-16 19:32:22 -0600258 bit_index += sample_index * bit_depth; /* Multiple channels: select one */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500259 }
260
261 /* Return the sample from the row as an integer. */
John Bowler168a4332011-01-16 19:32:22 -0600262 row += bit_index >> 3;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500263 result = *row;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500264
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500265 if (bit_depth == 8)
266 return result;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500267
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500268 else if (bit_depth > 8)
269 return (result << 8) + *++row;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500270
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500271 /* Less than 8 bits per sample. */
John Bowler168a4332011-01-16 19:32:22 -0600272 bit_index &= 7;
273 return (result >> (8-bit_index-bit_depth)) & ((1U<<bit_depth)-1);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500274}
275
John Bowler660c6e42010-12-19 06:22:23 -0600276/* Copy a single pixel, of a given size, from one buffer to another -
277 * while this is basically bit addressed there is an implicit assumption
278 * that pixels 8 or more bits in size are byte aligned and that pixels
279 * do not otherwise cross byte boundaries. (This is, so far as I know,
280 * universally true in bitmap computer graphics. [JCB 20101212])
281 *
282 * NOTE: The to and from buffers may be the same.
283 */
284static void
285pixel_copy(png_bytep toBuffer, png_uint_32 toIndex,
286 png_const_bytep fromBuffer, png_uint_32 fromIndex, unsigned int pixelSize)
287{
288 /* Assume we can multiply by 'size' without overflow because we are
289 * just working in a single buffer.
290 */
291 toIndex *= pixelSize;
292 fromIndex *= pixelSize;
293 if (pixelSize < 8) /* Sub-byte */
294 {
295 /* Mask to select the location of the copied pixel: */
296 unsigned int destMask = ((1U<<pixelSize)-1) << (8-pixelSize-(toIndex&7));
297 /* The following read the entire pixels and clears the extra: */
298 unsigned int destByte = toBuffer[toIndex >> 3] & ~destMask;
299 unsigned int sourceByte = fromBuffer[fromIndex >> 3];
300
301 /* Don't rely on << or >> supporting '0' here, just in case: */
302 fromIndex &= 7;
303 if (fromIndex > 0) sourceByte <<= fromIndex;
304 if ((toIndex & 7) > 0) sourceByte >>= toIndex & 7;
305
306 toBuffer[toIndex >> 3] = (png_byte)(destByte | (sourceByte & destMask));
307 }
308 else /* One or more bytes */
309 memmove(toBuffer+(toIndex>>3), fromBuffer+(fromIndex>>3), pixelSize>>3);
310}
311
312/* Compare pixels - they are assumed to start at the first byte in the
313 * given buffers.
314 */
315static int
316pixel_cmp(png_const_bytep pa, png_const_bytep pb, png_uint_32 bit_width)
317{
318 if (memcmp(pa, pb, bit_width>>3) == 0)
319 {
320 png_uint_32 p;
321
322 if ((bit_width & 7) == 0) return 0;
323
324 /* Ok, any differences? */
325 p = pa[bit_width >> 3];
326 p ^= pb[bit_width >> 3];
327
328 if (p == 0) return 0;
329
330 /* There are, but they may not be significant, remove the bits
331 * after the end (the low order bits in PNG.)
332 */
333 bit_width &= 7;
334 p >>= 8-bit_width;
335
336 if (p == 0) return 0;
337 }
338
339 return 1; /* Different */
340}
341
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500342/*************************** BASIC PNG FILE WRITING ***************************/
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500343/* A png_store takes data from the sequential writer or provides data
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500344 * to the sequential reader. It can also store the result of a PNG
345 * write for later retrieval.
346 */
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500347#define STORE_BUFFER_SIZE 500 /* arbitrary */
348typedef struct png_store_buffer
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500349{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500350 struct png_store_buffer* prev; /* NOTE: stored in reverse order */
351 png_byte buffer[STORE_BUFFER_SIZE];
352} png_store_buffer;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500353
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500354#define FILE_NAME_SIZE 64
355
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500356typedef struct png_store_file
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500357{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500358 struct png_store_file* next; /* as many as you like... */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500359 char name[FILE_NAME_SIZE];
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500360 png_uint_32 id; /* must be correct (see FILEID) */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500361 png_size_t datacount; /* In this (the last) buffer */
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500362 png_store_buffer data; /* Last buffer in file */
363} png_store_file;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500364
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500365/* The following is a pool of memory allocated by a single libpng read or write
366 * operation.
367 */
368typedef struct store_pool
369{
370 struct png_store *store; /* Back pointer */
371 struct store_memory *list; /* List of allocated memory */
372 png_byte mark[4]; /* Before and after data */
373
374 /* Statistics for this run. */
375 png_alloc_size_t max; /* Maximum single allocation */
376 png_alloc_size_t current; /* Current allocation */
377 png_alloc_size_t limit; /* Highest current allocation */
378 png_alloc_size_t total; /* Total allocation */
379
380 /* Overall statistics (retained across successive runs). */
381 png_alloc_size_t max_max;
382 png_alloc_size_t max_limit;
383 png_alloc_size_t max_total;
384} store_pool;
385
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500386typedef struct png_store
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500387{
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500388 /* For cexcept.h exception handling - simply store one of these;
389 * the context is a self pointer but it may point to a different
390 * png_store (in fact it never does in this program.)
391 */
392 struct exception_context
393 exception_context;
394
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -0500395 unsigned int verbose :1;
396 unsigned int treat_warnings_as_errors :1;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500397 unsigned int expect_error :1;
398 unsigned int expect_warning :1;
399 unsigned int saw_warning :1;
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500400 unsigned int speed :1;
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500401 unsigned int progressive :1; /* use progressive read */
402 unsigned int validated :1; /* used as a temporary flag */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500403 int nerrors;
404 int nwarnings;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500405 char test[64]; /* Name of test */
406 char error[128];
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500407
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500408 /* Read fields */
409 png_structp pread; /* Used to read a saved file */
410 png_infop piread;
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500411 png_store_file* current; /* Set when reading */
412 png_store_buffer* next; /* Set when reading */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500413 png_size_t readpos; /* Position in *next */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500414 png_byte* image; /* Buffer for reading interlaced images */
415 size_t cb_image; /* Size of this buffer */
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500416 store_pool read_memory_pool;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500417
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500418 /* Write fields */
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500419 png_store_file* saved;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500420 png_structp pwrite; /* Used when writing a new file */
421 png_infop piwrite;
422 png_size_t writepos; /* Position in .new */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500423 char wname[FILE_NAME_SIZE];
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500424 png_store_buffer new; /* The end of the new PNG file being written. */
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500425 store_pool write_memory_pool;
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500426} png_store;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500427
428/* Initialization and cleanup */
429static void
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500430store_pool_mark(png_byte *mark)
431{
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600432 /* Generate a new mark. This uses a boring repeatable algorithm and it is
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500433 * implemented here so that it gives the same set of numbers on every
434 * architecture. It's a linear congruential generator (Knuth or Sedgewick
435 * "Algorithms") but it comes from the 'feedback taps' table in Horowitz and
436 * Hill, "The Art of Electronics".
437 */
438 static png_uint_32 u0 = 0x12345678, u1 = 1;
439
440 /* There are thirty three bits, the next bit in the sequence is bit-33 XOR
441 * bit-20. The top 1 bit is in u1, the bottom 32 are in u0.
442 */
443 int i;
444 for (i=0; i<4; ++i)
445 {
446 /* First generate 8 new bits then shift them in at the end. */
447 png_uint_32 u = ((u0 >> (20-8)) ^ ((u1 << 7) | (u0 >> (32-7)))) & 0xff;
448 u1 <<= 8;
449 u1 |= u0 >> 24;
450 u0 <<= 8;
451 u0 |= u;
452 *mark++ = (png_byte)u;
453 }
454}
455
456static void
457store_pool_init(png_store *ps, store_pool *pool)
458{
459 memset(pool, 0, sizeof *pool);
460
461 pool->store = ps;
462 pool->list = NULL;
463 pool->max = pool->current = pool->limit = pool->total = 0;
464 pool->max_max = pool->max_limit = pool->max_total = 0;
465 store_pool_mark(pool->mark);
466}
467
468static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500469store_init(png_store* ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500470{
471 memset(ps, 0, sizeof *ps);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500472 init_exception_context(&ps->exception_context);
473 store_pool_init(ps, &ps->read_memory_pool);
474 store_pool_init(ps, &ps->write_memory_pool);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500475 ps->verbose = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500476 ps->treat_warnings_as_errors = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500477 ps->expect_error = 0;
478 ps->expect_warning = 0;
479 ps->saw_warning = 0;
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500480 ps->speed = 0;
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500481 ps->progressive = 0;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500482 ps->validated = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500483 ps->nerrors = ps->nwarnings = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500484 ps->pread = NULL;
485 ps->piread = NULL;
486 ps->saved = ps->current = NULL;
487 ps->next = NULL;
488 ps->readpos = 0;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500489 ps->image = NULL;
490 ps->cb_image = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500491 ps->pwrite = NULL;
492 ps->piwrite = NULL;
493 ps->writepos = 0;
494 ps->new.prev = NULL;
495}
496
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500497/* This somewhat odd function is used when reading an image to ensure that the
498 * buffer is big enough - this is why a png_structp is available.
499 */
500static void
501store_ensure_image(png_store *ps, png_structp pp, size_t cb)
502{
503 if (ps->cb_image < cb)
504 {
505 if (ps->image != NULL)
506 {
507 free(ps->image-1);
508 ps->cb_image = 0;
509 }
510
511 /* The buffer is deliberately mis-aligned. */
512 ps->image = malloc(cb+1);
513 if (ps->image == NULL)
514 png_error(pp, "OOM allocating image buffer");
515
516 ++(ps->image);
517 ps->cb_image = cb;
518 }
John Bowler660c6e42010-12-19 06:22:23 -0600519
520 /* And, for error checking, the whole buffer is set to '1' - this
521 * matches what happens with the 'size' test images on write and also
522 * matches the unused bits in the test rows.
523 */
524 memset(ps->image, 0xff, cb);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -0500525}
526
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500527static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500528store_freebuffer(png_store_buffer* psb)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500529{
530 if (psb->prev)
531 {
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500532 store_freebuffer(psb->prev);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500533 free(psb->prev);
534 psb->prev = NULL;
535 }
536}
537
538static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500539store_freenew(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500540{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500541 store_freebuffer(&ps->new);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500542 ps->writepos = 0;
543}
544
545static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500546store_storenew(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500547{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500548 png_store_buffer *pb;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500549
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500550 if (ps->writepos != STORE_BUFFER_SIZE)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500551 png_error(ps->pwrite, "invalid store call");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500552
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500553 pb = malloc(sizeof *pb);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500554
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500555 if (pb == NULL)
556 png_error(ps->pwrite, "store new: OOM");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500557
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500558 *pb = ps->new;
559 ps->new.prev = pb;
560 ps->writepos = 0;
561}
562
563static void
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500564store_freefile(png_store_file **ppf)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500565{
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500566 if (*ppf != NULL)
567 {
568 store_freefile(&(*ppf)->next);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500569
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500570 store_freebuffer(&(*ppf)->data);
571 (*ppf)->datacount = 0;
572 free(*ppf);
573 *ppf = NULL;
574 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500575}
576
577/* Main interface to file storeage, after writing a new PNG file (see the API
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500578 * below) call store_storefile to store the result with the given name and id.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500579 */
580static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500581store_storefile(png_store *ps, png_uint_32 id)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500582{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500583 png_store_file *pf = malloc(sizeof *pf);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500584 if (pf == NULL)
585 png_error(ps->pwrite, "storefile: OOM");
586 safecat(pf->name, sizeof pf->name, 0, ps->wname);
587 pf->id = id;
588 pf->data = ps->new;
589 pf->datacount = ps->writepos;
590 ps->new.prev = NULL;
591 ps->writepos = 0;
592
593 /* And save it. */
594 pf->next = ps->saved;
595 ps->saved = pf;
596}
597
598/* Generate an error message (in the given buffer) */
599static size_t
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500600store_message(png_store *ps, png_structp pp, char *buffer, size_t bufsize,
601 size_t pos, PNG_CONST char *msg)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500602{
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500603 if (pp != NULL && pp == ps->pread)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500604 {
605 /* Reading a file */
606 pos = safecat(buffer, bufsize, pos, "read: ");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500607
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500608 if (ps->current != NULL)
609 {
610 pos = safecat(buffer, bufsize, pos, ps->current->name);
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -0500611 pos = safecat(buffer, bufsize, pos, sep);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500612 }
613 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500614
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500615 else if (pp != NULL && pp == ps->pwrite)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500616 {
617 /* Writing a file */
618 pos = safecat(buffer, bufsize, pos, "write: ");
619 pos = safecat(buffer, bufsize, pos, ps->wname);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500620 pos = safecat(buffer, bufsize, pos, sep);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500621 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500622
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500623 else
624 {
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500625 /* Neither reading nor writing (or a memory error in struct delete) */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500626 pos = safecat(buffer, bufsize, pos, "pngvalid: ");
627 }
628
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500629 if (ps->test[0] != 0)
630 {
631 pos = safecat(buffer, bufsize, pos, ps->test);
632 pos = safecat(buffer, bufsize, pos, sep);
633 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500634 pos = safecat(buffer, bufsize, pos, msg);
635 return pos;
636}
637
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500638/* Log an error or warning - the relevant count is always incremented. */
639static void
640store_log(png_store* ps, png_structp pp, png_const_charp message, int is_error)
641{
642 /* The warning is copied to the error buffer if there are no errors and it is
643 * the first warning. The error is copied to the error buffer if it is the
644 * first error (overwriting any prior warnings).
645 */
646 if (is_error ? (ps->nerrors)++ == 0 :
647 (ps->nwarnings)++ == 0 && ps->nerrors == 0)
648 store_message(ps, pp, ps->error, sizeof ps->error, 0, message);
649
650 if (ps->verbose)
651 {
652 char buffer[256];
653 size_t pos;
654
655 if (is_error)
656 pos = safecat(buffer, sizeof buffer, 0, "error: ");
657 else
658 pos = safecat(buffer, sizeof buffer, 0, "warning: ");
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -0600659
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500660 store_message(ps, pp, buffer, sizeof buffer, pos, message);
661 fputs(buffer, stderr);
662 fputc('\n', stderr);
663 }
664}
665
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500666/* Functions to use as PNG callbacks. */
667static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500668store_error(png_structp pp, png_const_charp message) /* PNG_NORETURN */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500669{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500670 png_store *ps = png_get_error_ptr(pp);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500671
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500672 if (!ps->expect_error)
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600673 store_log(ps, pp, message, 1 /* error */);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500674
675 /* And finally throw an exception. */
676 {
677 struct exception_context *the_exception_context = &ps->exception_context;
678 Throw ps;
679 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500680}
681
682static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500683store_warning(png_structp pp, png_const_charp message)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500684{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500685 png_store *ps = png_get_error_ptr(pp);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500686
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500687 if (!ps->expect_warning)
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600688 store_log(ps, pp, message, 0 /* warning */);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500689 else
690 ps->saw_warning = 1;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500691}
692
693static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500694store_write(png_structp pp, png_bytep pb, png_size_t st)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500695{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500696 png_store *ps = png_get_io_ptr(pp);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500697
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500698 if (ps->pwrite != pp)
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500699 png_error(pp, "store state damaged");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500700
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500701 while (st > 0)
702 {
703 size_t cb;
704
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500705 if (ps->writepos >= STORE_BUFFER_SIZE)
706 store_storenew(ps);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500707
708 cb = st;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500709
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500710 if (cb > STORE_BUFFER_SIZE - ps->writepos)
711 cb = STORE_BUFFER_SIZE - ps->writepos;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500712
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500713 memcpy(ps->new.buffer + ps->writepos, pb, cb);
714 pb += cb;
715 st -= cb;
716 ps->writepos += cb;
717 }
718}
719
720static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500721store_flush(png_structp pp)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500722{
Glenn Randers-Pehrsond546f432010-12-04 20:41:36 -0600723 UNUSED(pp) /*DOES NOTHING*/
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500724}
725
726static size_t
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500727store_read_buffer_size(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500728{
729 /* Return the bytes available for read in the current buffer. */
730 if (ps->next != &ps->current->data)
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500731 return STORE_BUFFER_SIZE;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500732
733 return ps->current->datacount;
734}
735
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500736/* Return total bytes available for read. */
737static size_t
738store_read_buffer_avail(png_store *ps)
739{
740 if (ps->current != NULL && ps->next != NULL)
741 {
742 png_store_buffer *next = &ps->current->data;
743 size_t cbAvail = ps->current->datacount;
744
745 while (next != ps->next && next != NULL)
746 {
747 next = next->prev;
748 cbAvail += STORE_BUFFER_SIZE;
749 }
750
751 if (next != ps->next)
752 png_error(ps->pread, "buffer read error");
753
754 if (cbAvail > ps->readpos)
755 return cbAvail - ps->readpos;
756 }
757
758 return 0;
759}
760
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500761static int
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500762store_read_buffer_next(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500763{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500764 png_store_buffer *pbOld = ps->next;
765 png_store_buffer *pbNew = &ps->current->data;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500766 if (pbOld != pbNew)
767 {
768 while (pbNew != NULL && pbNew->prev != pbOld)
769 pbNew = pbNew->prev;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500770
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500771 if (pbNew != NULL)
772 {
773 ps->next = pbNew;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -0500774 ps->readpos = 0;
775 return 1;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500776 }
777
778 png_error(ps->pread, "buffer lost");
779 }
780
781 return 0; /* EOF or error */
782}
783
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500784/* Need separate implementation and callback to allow use of the same code
785 * during progressive read, where the io_ptr is set internally by libpng.
786 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500787static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500788store_read_imp(png_store *ps, png_bytep pb, png_size_t st)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500789{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500790 if (ps->current == NULL || ps->next == NULL)
791 png_error(ps->pread, "store state damaged");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500792
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500793 while (st > 0)
794 {
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500795 size_t cbAvail = store_read_buffer_size(ps) - ps->readpos;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500796
797 if (cbAvail > 0)
798 {
799 if (cbAvail > st) cbAvail = st;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -0500800 memcpy(pb, ps->next->buffer + ps->readpos, cbAvail);
801 st -= cbAvail;
802 pb += cbAvail;
803 ps->readpos += cbAvail;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500804 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -0500805
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -0500806 else if (!store_read_buffer_next(ps))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500807 png_error(ps->pread, "read beyond end of file");
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -0500808 }
809}
810
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500811static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500812store_read(png_structp pp, png_bytep pb, png_size_t st)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500813{
814 png_store *ps = png_get_io_ptr(pp);
815
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500816 if (ps == NULL || ps->pread != pp)
817 png_error(pp, "bad store read call");
818
819 store_read_imp(ps, pb, st);
820}
821
822static void
823store_progressive_read(png_store *ps, png_structp pp, png_infop pi)
824{
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500825 /* Notice that a call to store_read will cause this function to fail because
826 * readpos will be set.
827 */
828 if (ps->pread != pp || ps->current == NULL || ps->next == NULL)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -0500829 png_error(pp, "store state damaged (progressive)");
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500830
831 do
832 {
833 if (ps->readpos != 0)
834 png_error(pp, "store_read called during progressive read");
835
836 png_process_data(pp, pi, ps->next->buffer, store_read_buffer_size(ps));
837 }
838 while (store_read_buffer_next(ps));
839}
840
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500841/***************************** MEMORY MANAGEMENT*** ***************************/
842/* A store_memory is simply the header for an allocated block of memory. The
843 * pointer returned to libpng is just after the end of the header block, the
844 * allocated memory is followed by a second copy of the 'mark'.
845 */
846typedef struct store_memory
847{
848 store_pool *pool; /* Originating pool */
849 struct store_memory *next; /* Singly linked list */
850 png_alloc_size_t size; /* Size of memory allocated */
851 png_byte mark[4]; /* ID marker */
852} store_memory;
853
854/* Handle a fatal error in memory allocation. This calls png_error if the
855 * libpng struct is non-NULL, else it outputs a message and returns. This means
856 * that a memory problem while libpng is running will abort (png_error) the
857 * handling of particular file while one in cleanup (after the destroy of the
858 * struct has returned) will simply keep going and free (or attempt to free)
859 * all the memory.
860 */
861static void
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500862store_pool_error(png_store *ps, png_structp pp, PNG_CONST char *msg)
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500863{
864 if (pp != NULL)
865 png_error(pp, msg);
866
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -0500867 /* Else we have to do it ourselves. png_error eventually calls store_log,
868 * above. store_log accepts a NULL png_structp - it just changes what gets
869 * output by store_message.
870 */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600871 store_log(ps, pp, msg, 1 /* error */);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500872}
873
874static void
875store_memory_free(png_structp pp, store_pool *pool, store_memory *memory)
876{
877 /* Note that pp may be NULL (see store_pool_delete below), the caller has
878 * found 'memory' in pool->list *and* unlinked this entry, so this is a valid
879 * pointer (for sure), but the contents may have been trashed.
880 */
881 if (memory->pool != pool)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500882 store_pool_error(pool->store, pp, "memory corrupted (pool)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500883
884 else if (memcmp(memory->mark, pool->mark, sizeof memory->mark) != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500885 store_pool_error(pool->store, pp, "memory corrupted (start)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500886
887 /* It should be safe to read the size field now. */
888 else
889 {
890 png_alloc_size_t cb = memory->size;
891
892 if (cb > pool->max)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500893 store_pool_error(pool->store, pp, "memory corrupted (size)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500894
895 else if (memcmp((png_bytep)(memory+1)+cb, pool->mark, sizeof pool->mark)
896 != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500897 store_pool_error(pool->store, pp, "memory corrupted (end)");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500898
899 /* Finally give the library a chance to find problems too: */
900 else
901 {
902 pool->current -= cb;
903 free(memory);
904 }
905 }
906}
907
908static void
909store_pool_delete(png_store *ps, store_pool *pool)
910{
911 if (pool->list != NULL)
912 {
913 fprintf(stderr, "%s: %s %s: memory lost (list follows):\n", ps->test,
914 pool == &ps->read_memory_pool ? "read" : "write",
915 pool == &ps->read_memory_pool ? (ps->current != NULL ?
916 ps->current->name : "unknown file") : ps->wname);
917 ++ps->nerrors;
918
919 do
920 {
921 store_memory *next = pool->list;
922 pool->list = next->next;
923 next->next = NULL;
924
Glenn Randers-Pehrson9a75d992010-10-08 16:27:14 -0500925 fprintf(stderr, "\t%lu bytes @ %p\n",
926 (unsigned long)next->size, next+1);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500927 /* The NULL means this will always return, even if the memory is
928 * corrupted.
929 */
930 store_memory_free(NULL, pool, next);
931 }
932 while (pool->list != NULL);
933 }
934
935 /* And reset the other fields too for the next time. */
936 if (pool->max > pool->max_max) pool->max_max = pool->max;
937 pool->max = 0;
938 if (pool->current != 0) /* unexpected internal error */
939 fprintf(stderr, "%s: %s %s: memory counter mismatch (internal error)\n",
940 ps->test, pool == &ps->read_memory_pool ? "read" : "write",
941 pool == &ps->read_memory_pool ? (ps->current != NULL ?
942 ps->current->name : "unknown file") : ps->wname);
943 pool->current = 0;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600944
945 if (pool->limit > pool->max_limit)
946 pool->max_limit = pool->limit;
947
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500948 pool->limit = 0;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600949
950 if (pool->total > pool->max_total)
951 pool->max_total = pool->total;
952
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500953 pool->total = 0;
954
955 /* Get a new mark too. */
956 store_pool_mark(pool->mark);
957}
958
959/* The memory callbacks: */
960static png_voidp
961store_malloc(png_structp pp, png_alloc_size_t cb)
962{
963 store_pool *pool = png_get_mem_ptr(pp);
964 store_memory *new = malloc(cb + (sizeof *new) + (sizeof pool->mark));
965
966 if (new != NULL)
967 {
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600968 if (cb > pool->max)
969 pool->max = cb;
970
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500971 pool->current += cb;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600972
973 if (pool->current > pool->limit)
974 pool->limit = pool->current;
975
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500976 pool->total += cb;
977
978 new->size = cb;
979 memcpy(new->mark, pool->mark, sizeof new->mark);
980 memcpy((png_byte*)(new+1) + cb, pool->mark, sizeof pool->mark);
981 new->pool = pool;
982 new->next = pool->list;
983 pool->list = new;
984 ++new;
985 }
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -0600986
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500987 else
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -0500988 store_pool_error(pool->store, pp, "out of memory");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -0500989
990 return new;
991}
992
993static void
994store_free(png_structp pp, png_voidp memory)
995{
996 store_pool *pool = png_get_mem_ptr(pp);
997 store_memory *this = memory, **test;
998
999 /* First check that this 'memory' really is valid memory - it must be in the
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001000 * pool list. If it is, use the shared memory_free function to free it.
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001001 */
1002 --this;
1003 for (test = &pool->list; *test != this; test = &(*test)->next)
1004 {
1005 if (*test == NULL)
1006 {
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05001007 store_pool_error(pool->store, pp, "bad pointer to free");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001008 return;
1009 }
1010 }
1011
1012 /* Unlink this entry, *test == this. */
1013 *test = this->next;
1014 this->next = NULL;
1015 store_memory_free(pp, pool, this);
1016}
1017
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001018/* Setup functions. */
1019/* Cleanup when aborting a write or after storing the new file. */
1020static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001021store_write_reset(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001022{
1023 if (ps->pwrite != NULL)
1024 {
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001025 anon_context(ps);
1026
1027 Try
1028 png_destroy_write_struct(&ps->pwrite, &ps->piwrite);
1029
1030 Catch_anonymous
1031 {
1032 /* memory corruption: continue. */
1033 }
1034
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001035 ps->pwrite = NULL;
1036 ps->piwrite = NULL;
1037 }
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001038
1039 /* And make sure that all the memory has been freed - this will output
1040 * spurious errors in the case of memory corruption above, but this is safe.
1041 */
1042 store_pool_delete(ps, &ps->write_memory_pool);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001043
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001044 store_freenew(ps);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001045}
1046
1047/* The following is the main write function, it returns a png_struct and,
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001048 * optionally, a png_info suitable for writiing a new PNG file. Use
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001049 * store_storefile above to record this file after it has been written. The
1050 * returned libpng structures as destroyed by store_write_reset above.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001051 */
1052static png_structp
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001053set_store_for_write(png_store *ps, png_infopp ppi,
1054 PNG_CONST char * volatile name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001055{
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001056 anon_context(ps);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001057
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001058 Try
1059 {
1060 if (ps->pwrite != NULL)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001061 png_error(ps->pwrite, "write store already in use");
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001062
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001063 store_write_reset(ps);
1064 safecat(ps->wname, sizeof ps->wname, 0, name);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001065
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001066 /* Don't do the slow memory checks if doing a speed test. */
1067 if (ps->speed)
1068 ps->pwrite = png_create_write_struct(PNG_LIBPNG_VER_STRING,
1069 ps, store_error, store_warning);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001070
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001071 else
1072 ps->pwrite = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
1073 ps, store_error, store_warning, &ps->write_memory_pool,
1074 store_malloc, store_free);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001075
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001076 png_set_write_fn(ps->pwrite, ps, store_write, store_flush);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001077
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001078 if (ppi != NULL)
1079 *ppi = ps->piwrite = png_create_info_struct(ps->pwrite);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001080 }
1081
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001082 Catch_anonymous
1083 return NULL;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001084
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001085 return ps->pwrite;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001086}
1087
Glenn Randers-Pehrsonbc363ec2010-10-12 21:17:00 -05001088/* Cleanup when finished reading (either due to error or in the success case).
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001089 */
1090static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001091store_read_reset(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001092{
1093 if (ps->pread != NULL)
1094 {
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001095 anon_context(ps);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001096
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001097 Try
1098 png_destroy_read_struct(&ps->pread, &ps->piread, NULL);
1099
1100 Catch_anonymous
1101 {
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001102 /* error already output: continue */
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001103 }
1104
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001105 ps->pread = NULL;
1106 ps->piread = NULL;
1107 }
1108
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001109 /* Always do this to be safe. */
1110 store_pool_delete(ps, &ps->read_memory_pool);
1111
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001112 ps->current = NULL;
1113 ps->next = NULL;
1114 ps->readpos = 0;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001115 ps->validated = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001116}
1117
1118static void
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001119store_read_set(png_store *ps, png_uint_32 id)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001120{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001121 png_store_file *pf = ps->saved;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001122
1123 while (pf != NULL)
1124 {
1125 if (pf->id == id)
1126 {
1127 ps->current = pf;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001128 ps->next = NULL;
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001129 store_read_buffer_next(ps);
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001130 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001131 }
1132
1133 pf = pf->next;
1134 }
1135
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001136 {
1137 size_t pos;
1138 char msg[FILE_NAME_SIZE+64];
1139
1140 pos = standard_name_from_id(msg, sizeof msg, 0, id);
1141 pos = safecat(msg, sizeof msg, pos, ": file not found");
1142 png_error(ps->pread, msg);
1143 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001144}
1145
1146/* The main interface for reading a saved file - pass the id number of the file
1147 * to retrieve. Ids must be unique or the earlier file will be hidden. The API
1148 * returns a png_struct and, optionally, a png_info. Both of these will be
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001149 * destroyed by store_read_reset above.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001150 */
1151static png_structp
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001152set_store_for_read(png_store *ps, png_infopp ppi, png_uint_32 id,
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001153 PNG_CONST char *name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001154{
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001155 /* Set the name for png_error */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001156 safecat(ps->test, sizeof ps->test, 0, name);
1157
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001158 if (ps->pread != NULL)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001159 png_error(ps->pread, "read store already in use");
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001160
1161 store_read_reset(ps);
1162
1163 /* Both the create APIs can return NULL if used in their default mode
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001164 * (because there is no other way of handling an error because the jmp_buf
1165 * by default is stored in png_struct and that has not been allocated!)
1166 * However, given that store_error works correctly in these circumstances
1167 * we don't ever expect NULL in this program.
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001168 */
1169 if (ps->speed)
1170 ps->pread = png_create_read_struct(PNG_LIBPNG_VER_STRING, ps,
1171 store_error, store_warning);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001172
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001173 else
1174 ps->pread = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, ps,
1175 store_error, store_warning, &ps->read_memory_pool, store_malloc,
1176 store_free);
1177
1178 if (ps->pread == NULL)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001179 {
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001180 struct exception_context *the_exception_context = &ps->exception_context;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001181
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05001182 store_log(ps, NULL, "png_create_read_struct returned NULL (unexpected)",
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001183 1 /*error*/);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001184
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001185 Throw ps;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001186 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001187
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001188 store_read_set(ps, id);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001189
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001190 if (ppi != NULL)
1191 *ppi = ps->piread = png_create_info_struct(ps->pread);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001192
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001193 return ps->pread;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001194}
1195
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001196/* The overall cleanup of a store simply calls the above then removes all the
1197 * saved files. This does not delete the store itself.
1198 */
1199static void
1200store_delete(png_store *ps)
1201{
1202 store_write_reset(ps);
1203 store_read_reset(ps);
1204 store_freefile(&ps->saved);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001205
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001206 if (ps->image != NULL)
1207 {
1208 free(ps->image-1);
1209 ps->image = NULL;
1210 ps->cb_image = 0;
1211 }
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05001212}
1213
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001214/*********************** PNG FILE MODIFICATION ON READ ************************/
1215/* Files may be modified on read. The following structure contains a complete
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001216 * png_store together with extra members to handle modification and a special
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001217 * read callback for libpng. To use this the 'modifications' field must be set
1218 * to a list of png_modification structures that actually perform the
1219 * modification, otherwise a png_modifier is functionally equivalent to a
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001220 * png_store. There is a special read function, set_modifier_for_read, which
1221 * replaces set_store_for_read.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001222 */
1223typedef struct png_modifier
1224{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001225 png_store this; /* I am a png_store */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001226 struct png_modification *modifications; /* Changes to make */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001227
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001228 enum modifier_state
1229 {
1230 modifier_start, /* Initial value */
1231 modifier_signature, /* Have a signature */
1232 modifier_IHDR /* Have an IHDR */
1233 } state; /* My state */
1234
1235 /* Information from IHDR: */
1236 png_byte bit_depth; /* From IHDR */
1237 png_byte colour_type; /* From IHDR */
1238
1239 /* While handling PLTE, IDAT and IEND these chunks may be pended to allow
1240 * other chunks to be inserted.
1241 */
1242 png_uint_32 pending_len;
1243 png_uint_32 pending_chunk;
1244
1245 /* Test values */
1246 double *gammas;
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001247 unsigned int ngammas;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001248
1249 /* Lowest sbit to test (libpng fails for sbit < 8) */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001250 png_byte sbitlow;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001251
1252 /* Error control - these are the limits on errors accepted by the gamma tests
1253 * below.
1254 */
1255 double maxout8; /* Maximum output value error */
Glenn Randers-Pehrson21b4b332010-08-18 07:12:38 -05001256 double maxabs8; /* Absolute sample error 0..1 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001257 double maxpc8; /* Percentage sample error 0..100% */
1258 double maxout16; /* Maximum output value error */
1259 double maxabs16; /* Absolute sample error 0..1 */
1260 double maxpc16; /* Percentage sample error 0..100% */
1261
1262 /* Logged 8 and 16 bit errors ('output' values): */
1263 double error_gray_2;
1264 double error_gray_4;
1265 double error_gray_8;
1266 double error_gray_16;
1267 double error_color_8;
1268 double error_color_16;
1269
1270 /* Flags: */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001271 /* Whether or not to interlace. */
1272 int interlace_type :9; /* int, but must store '1' */
1273
John Bowlerb54498e2010-12-08 16:26:21 -06001274 /* Run the standard tests? */
1275 unsigned int test_standard :1;
1276
John Bowler660c6e42010-12-19 06:22:23 -06001277 /* Run the odd-sized image and interlace read/write tests? */
1278 unsigned int test_size :1;
1279
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001280 /* When to use the use_input_precision option: */
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001281 unsigned int use_input_precision :1;
1282 unsigned int use_input_precision_sbit :1;
1283 unsigned int use_input_precision_16to8 :1;
John Bowlerb54498e2010-12-08 16:26:21 -06001284
1285 /* Which gamma tests to run: */
1286 unsigned int test_threshold :1;
1287 unsigned int test_transform :1; /* main tests */
1288 unsigned int test_sbit :1;
1289 unsigned int test_strip16 :1;
1290
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001291 unsigned int log :1; /* Log max error */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001292
1293 /* Buffer information, the buffer size limits the size of the chunks that can
1294 * be modified - they must fit (including header and CRC) into the buffer!
1295 */
1296 size_t flush; /* Count of bytes to flush */
1297 size_t buffer_count; /* Bytes in buffer */
1298 size_t buffer_position; /* Position in buffer */
1299 png_byte buffer[1024];
1300} png_modifier;
1301
1302static double abserr(png_modifier *pm, png_byte bit_depth)
1303{
1304 return bit_depth == 16 ? pm->maxabs16 : pm->maxabs8;
1305}
1306
1307static double pcerr(png_modifier *pm, png_byte bit_depth)
1308{
1309 return (bit_depth == 16 ? pm->maxpc16 : pm->maxpc8) * .01;
1310}
1311
1312static double outerr(png_modifier *pm, png_byte bit_depth)
1313{
1314 /* There is a serious error in the 2 and 4 bit grayscale transform because
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001315 * the gamma table value (8 bits) is simply shifted, not rounded, so the
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001316 * error in 4 bit greyscale gamma is up to the value below. This is a hack
1317 * to allow pngvalid to succeed:
1318 */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001319 if (bit_depth == 2)
1320 return .73182-.5;
1321
1322 if (bit_depth == 4)
1323 return .90644-.5;
1324
1325 if (bit_depth == 16)
1326 return pm->maxout16;
1327
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001328 return pm->maxout8;
1329}
1330
1331/* This returns true if the test should be stopped now because it has already
1332 * failed and it is running silently.
1333 */
1334static int fail(png_modifier *pm)
1335{
1336 return !pm->log && !pm->this.verbose && (pm->this.nerrors > 0 ||
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001337 (pm->this.treat_warnings_as_errors && pm->this.nwarnings > 0));
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001338}
1339
1340static void
1341modifier_init(png_modifier *pm)
1342{
1343 memset(pm, 0, sizeof *pm);
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001344 store_init(&pm->this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001345 pm->modifications = NULL;
1346 pm->state = modifier_start;
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001347 pm->sbitlow = 1U;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001348 pm->maxout8 = pm->maxpc8 = pm->maxabs8 = 0;
1349 pm->maxout16 = pm->maxpc16 = pm->maxabs16 = 0;
1350 pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0;
1351 pm->error_gray_16 = pm->error_color_8 = pm->error_color_16 = 0;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001352 pm->interlace_type = PNG_INTERLACE_NONE;
John Bowlerb54498e2010-12-08 16:26:21 -06001353 pm->test_standard = 1;
John Bowler660c6e42010-12-19 06:22:23 -06001354 pm->test_size = 0;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001355 pm->use_input_precision = 0;
1356 pm->use_input_precision_sbit = 0;
1357 pm->use_input_precision_16to8 = 0;
John Bowlerb54498e2010-12-08 16:26:21 -06001358 pm->test_threshold = 1;
1359 pm->test_transform = 1;
1360 pm->test_sbit = 1;
1361 pm->test_strip16 = 1;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001362 pm->log = 0;
1363
1364 /* Rely on the memset for all the other fields - there are no pointers */
1365}
1366
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05001367/* One modification structure must be provided for each chunk to be modified (in
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001368 * fact more than one can be provided if multiple separate changes are desired
1369 * for a single chunk.) Modifications include adding a new chunk when a
1370 * suitable chunk does not exist.
1371 *
1372 * The caller of modify_fn will reset the CRC of the chunk and record 'modified'
1373 * or 'added' as appropriate if the modify_fn returns 1 (true). If the
1374 * modify_fn is NULL the chunk is simply removed.
1375 */
1376typedef struct png_modification
1377{
1378 struct png_modification *next;
1379 png_uint_32 chunk;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001380
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001381 /* If the following is NULL all matching chunks will be removed: */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001382 int (*modify_fn)(struct png_modifier *pm,
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001383 struct png_modification *me, int add);
1384
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001385 /* If the following is set to PLTE, IDAT or IEND and the chunk has not been
1386 * found and modified (and there is a modify_fn) the modify_fn will be called
1387 * to add the chunk before the relevant chunk.
1388 */
1389 png_uint_32 add;
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05001390 unsigned int modified :1; /* Chunk was modified */
1391 unsigned int added :1; /* Chunk was added */
1392 unsigned int removed :1; /* Chunk was removed */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001393} png_modification;
1394
1395static void modification_reset(png_modification *pmm)
1396{
1397 if (pmm != NULL)
1398 {
1399 pmm->modified = 0;
1400 pmm->added = 0;
1401 pmm->removed = 0;
1402 modification_reset(pmm->next);
1403 }
1404}
1405
1406static void
1407modification_init(png_modification *pmm)
1408{
1409 memset(pmm, 0, sizeof *pmm);
1410 pmm->next = NULL;
1411 pmm->chunk = 0;
1412 pmm->modify_fn = NULL;
1413 pmm->add = 0;
1414 modification_reset(pmm);
1415}
1416
1417static void
1418modifier_reset(png_modifier *pm)
1419{
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001420 store_read_reset(&pm->this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001421 pm->modifications = NULL;
1422 pm->state = modifier_start;
1423 pm->bit_depth = pm->colour_type = 0;
1424 pm->pending_len = pm->pending_chunk = 0;
1425 pm->flush = pm->buffer_count = pm->buffer_position = 0;
1426}
1427
1428/* Convenience macros. */
1429#define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
1430#define CHUNK_IHDR CHUNK(73,72,68,82)
1431#define CHUNK_PLTE CHUNK(80,76,84,69)
1432#define CHUNK_IDAT CHUNK(73,68,65,84)
1433#define CHUNK_IEND CHUNK(73,69,78,68)
1434#define CHUNK_cHRM CHUNK(99,72,82,77)
1435#define CHUNK_gAMA CHUNK(103,65,77,65)
1436#define CHUNK_sBIT CHUNK(115,66,73,84)
1437#define CHUNK_sRGB CHUNK(115,82,71,66)
1438
1439/* The guts of modification are performed during a read. */
1440static void
1441modifier_crc(png_bytep buffer)
1442{
1443 /* Recalculate the chunk CRC - a complete chunk must be in
1444 * the buffer, at the start.
1445 */
1446 uInt datalen = png_get_uint_32(buffer);
1447 png_save_uint_32(buffer+datalen+8, crc32(0L, buffer+4, datalen+4));
1448}
1449
1450static void
1451modifier_setbuffer(png_modifier *pm)
1452{
1453 modifier_crc(pm->buffer);
1454 pm->buffer_count = png_get_uint_32(pm->buffer)+12;
1455 pm->buffer_position = 0;
1456}
1457
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001458/* Separate the callback into the actual implementation (which is passed the
1459 * png_modifier explicitly) and the callback, which gets the modifier from the
1460 * png_struct.
1461 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001462static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001463modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001464{
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001465 while (st > 0)
1466 {
1467 size_t cb;
1468 png_uint_32 len, chunk;
1469 png_modification *mod;
1470
1471 if (pm->buffer_position >= pm->buffer_count) switch (pm->state)
1472 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001473 static png_byte sign[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
1474 case modifier_start:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001475 store_read_imp(&pm->this, pm->buffer, 8); /* size of signature. */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001476 pm->buffer_count = 8;
1477 pm->buffer_position = 0;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001478
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001479 if (memcmp(pm->buffer, sign, 8) != 0)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001480 png_error(pm->this.pread, "invalid PNG file signature");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001481 pm->state = modifier_signature;
1482 break;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001483
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001484 case modifier_signature:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001485 store_read_imp(&pm->this, pm->buffer, 13+12); /* size of IHDR */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001486 pm->buffer_count = 13+12;
1487 pm->buffer_position = 0;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001488
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001489 if (png_get_uint_32(pm->buffer) != 13 ||
1490 png_get_uint_32(pm->buffer+4) != CHUNK_IHDR)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001491 png_error(pm->this.pread, "invalid IHDR");
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001492
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001493 /* Check the list of modifiers for modifications to the IHDR. */
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001494 mod = pm->modifications;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001495 while (mod != NULL)
1496 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001497 if (mod->chunk == CHUNK_IHDR && mod->modify_fn &&
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001498 (*mod->modify_fn)(pm, mod, 0))
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001499 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001500 mod->modified = 1;
1501 modifier_setbuffer(pm);
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001502 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001503
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001504 /* Ignore removal or add if IHDR! */
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001505 mod = mod->next;
1506 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001507
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001508 /* Cache information from the IHDR (the modified one.) */
1509 pm->bit_depth = pm->buffer[8+8];
1510 pm->colour_type = pm->buffer[8+8+1];
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001511
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001512 pm->state = modifier_IHDR;
1513 pm->flush = 0;
1514 break;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001515
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001516 case modifier_IHDR:
1517 default:
1518 /* Read a new chunk and process it until we see PLTE, IDAT or
1519 * IEND. 'flush' indicates that there is still some data to
1520 * output from the preceding chunk.
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001521 */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001522 if ((cb = pm->flush) > 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001523 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001524 if (cb > st) cb = st;
1525 pm->flush -= cb;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001526 store_read_imp(&pm->this, pb, cb);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001527 pb += cb;
1528 st -= cb;
1529 if (st <= 0) return;
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05001530 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001531
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001532 /* No more bytes to flush, read a header, or handle a pending
1533 * chunk.
1534 */
1535 if (pm->pending_chunk != 0)
1536 {
1537 png_save_uint_32(pm->buffer, pm->pending_len);
1538 png_save_uint_32(pm->buffer+4, pm->pending_chunk);
1539 pm->pending_len = 0;
1540 pm->pending_chunk = 0;
1541 }
1542 else
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001543 store_read_imp(&pm->this, pm->buffer, 8);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001544
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001545 pm->buffer_count = 8;
1546 pm->buffer_position = 0;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001547
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001548 /* Check for something to modify or a terminator chunk. */
1549 len = png_get_uint_32(pm->buffer);
1550 chunk = png_get_uint_32(pm->buffer+4);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001551
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001552 /* Terminators first, they may have to be delayed for added
1553 * chunks
1554 */
1555 if (chunk == CHUNK_PLTE || chunk == CHUNK_IDAT ||
1556 chunk == CHUNK_IEND)
1557 {
1558 mod = pm->modifications;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001559
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001560 while (mod != NULL)
1561 {
1562 if ((mod->add == chunk ||
1563 (mod->add == CHUNK_PLTE && chunk == CHUNK_IDAT)) &&
1564 mod->modify_fn != NULL && !mod->modified && !mod->added)
1565 {
1566 /* Regardless of what the modify function does do not run
1567 * this again.
1568 */
1569 mod->added = 1;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001570
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001571 if ((*mod->modify_fn)(pm, mod, 1 /*add*/))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001572 {
1573 /* Reset the CRC on a new chunk */
1574 if (pm->buffer_count > 0)
1575 modifier_setbuffer(pm);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001576
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001577 else
1578 {
1579 pm->buffer_position = 0;
1580 mod->removed = 1;
1581 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001582
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001583 /* The buffer has been filled with something (we assume)
1584 * so output this. Pend the current chunk.
1585 */
1586 pm->pending_len = len;
1587 pm->pending_chunk = chunk;
1588 break; /* out of while */
1589 }
1590 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001591
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001592 mod = mod->next;
1593 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001594
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001595 /* Don't do any further processing if the buffer was modified -
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001596 * otherwise the code will end up modifying a chunk that was
1597 * just added.
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001598 */
1599 if (mod != NULL)
1600 break; /* out of switch */
1601 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001602
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001603 /* If we get to here then this chunk may need to be modified. To
1604 * do this it must be less than 1024 bytes in total size, otherwise
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001605 * it just gets flushed.
1606 */
1607 if (len+12 <= sizeof pm->buffer)
1608 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001609 store_read_imp(&pm->this, pm->buffer+pm->buffer_count,
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001610 len+12-pm->buffer_count);
1611 pm->buffer_count = len+12;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001612
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001613 /* Check for a modification, else leave it be. */
1614 mod = pm->modifications;
1615 while (mod != NULL)
1616 {
1617 if (mod->chunk == chunk)
1618 {
1619 if (mod->modify_fn == NULL)
1620 {
1621 /* Remove this chunk */
1622 pm->buffer_count = pm->buffer_position = 0;
1623 mod->removed = 1;
1624 break; /* Terminate the while loop */
1625 }
1626
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001627 else if ((*mod->modify_fn)(pm, mod, 0))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001628 {
1629 mod->modified = 1;
1630 /* The chunk may have been removed: */
1631 if (pm->buffer_count == 0)
1632 {
1633 pm->buffer_position = 0;
1634 break;
1635 }
1636 modifier_setbuffer(pm);
1637 }
1638 }
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001639
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001640 mod = mod->next;
1641 }
1642 }
1643
1644 else
1645 pm->flush = len+12 - pm->buffer_count; /* data + crc */
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06001646
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001647 /* Take the data from the buffer (if there is any). */
1648 break;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001649 }
1650
1651 /* Here to read from the modifier buffer (not directly from
Glenn Randers-Pehrsonb4e69972010-07-30 10:35:38 -05001652 * the store, as in the flush case above.)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001653 */
1654 cb = pm->buffer_count - pm->buffer_position;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001655
1656 if (cb > st)
1657 cb = st;
1658
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001659 memcpy(pb, pm->buffer + pm->buffer_position, cb);
1660 st -= cb;
1661 pb += cb;
1662 pm->buffer_position += cb;
1663 }
1664}
1665
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001666/* The callback: */
1667static void
1668modifier_read(png_structp pp, png_bytep pb, png_size_t st)
1669{
1670 png_modifier *pm = png_get_io_ptr(pp);
1671
1672 if (pm == NULL || pm->this.pread != pp)
1673 png_error(pp, "bad modifier_read call");
1674
1675 modifier_read_imp(pm, pb, st);
1676}
1677
1678/* Like store_progressive_read but the data is getting changed as we go so we
1679 * need a local buffer.
1680 */
1681static void
1682modifier_progressive_read(png_modifier *pm, png_structp pp, png_infop pi)
1683{
1684 if (pm->this.pread != pp || pm->this.current == NULL ||
1685 pm->this.next == NULL)
1686 png_error(pp, "store state damaged (progressive)");
1687
1688 /* This is another Horowitz and Hill random noise generator. In this case
1689 * the aim is to stress the progressive reader with truely horrible variable
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06001690 * buffer sizes in the range 1..500, so a sequence of 9 bit random numbers
1691 * is generated. We could probably just count from 1 to 32767 and get as
1692 * good a result.
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05001693 */
1694 for (;;)
1695 {
1696 static png_uint_32 noise = 1;
1697 png_size_t cb, cbAvail;
1698 png_byte buffer[512];
1699
1700 /* Generate 15 more bits of stuff: */
1701 noise = (noise << 9) | ((noise ^ (noise >> (9-5))) & 0x1ff);
1702 cb = noise & 0x1ff;
1703
1704 /* Check that this number of bytes are available (in the current buffer.)
1705 * (This doesn't quite work - the modifier might delete a chunk; unlikely
1706 * but possible, it doesn't happen at present because the modifier only
1707 * adds chunks to standard images.)
1708 */
1709 cbAvail = store_read_buffer_avail(&pm->this);
1710 if (pm->buffer_count > pm->buffer_position)
1711 cbAvail += pm->buffer_count - pm->buffer_position;
1712
1713 if (cb > cbAvail)
1714 {
1715 /* Check for EOF: */
1716 if (cbAvail == 0)
1717 break;
1718
1719 cb = cbAvail;
1720 }
1721
1722 modifier_read_imp(pm, buffer, cb);
1723 png_process_data(pp, pi, buffer, cb);
1724 }
1725
1726 /* Check the invariants at the end (if this fails it's a problem in this
1727 * file!)
1728 */
1729 if (pm->buffer_count > pm->buffer_position ||
1730 pm->this.next != &pm->this.current->data ||
1731 pm->this.readpos < pm->this.current->datacount)
1732 png_error(pp, "progressive read implementation error");
1733}
1734
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001735/* Set up a modifier. */
1736static png_structp
1737set_modifier_for_read(png_modifier *pm, png_infopp ppi, png_uint_32 id,
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001738 PNG_CONST char *name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001739{
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001740 /* Do this first so that the modifier fields are cleared even if an error
1741 * happens allocating the png_struct. No allocation is done here so no
1742 * cleanup is required.
1743 */
1744 pm->state = modifier_start;
1745 pm->bit_depth = 0;
1746 pm->colour_type = 255;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001747
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001748 pm->pending_len = 0;
1749 pm->pending_chunk = 0;
1750 pm->flush = 0;
1751 pm->buffer_count = 0;
1752 pm->buffer_position = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001753
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05001754 return set_store_for_read(&pm->this, ppi, id, name);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001755}
1756
1757/***************************** STANDARD PNG FILES *****************************/
1758/* Standard files - write and save standard files. */
John Bowler660c6e42010-12-19 06:22:23 -06001759/* There are two basic forms of standard images. Those which attempt to have
1760 * all the possible pixel values (not possible for 16bpp images, but a range of
1761 * values are produced) and those which have a range of image sizes. The former
1762 * are used for testing transforms, in particular gamma correction and bit
1763 * reduction and increase. The latter are reserved for testing the behavior of
1764 * libpng with respect to 'odd' image sizes - particularly small images where
1765 * rows become 1 byte and interlace passes disappear.
1766 *
1767 * The first, most useful, set are the 'transform' images, the second set of
1768 * small images are the 'size' images.
1769 *
1770 * The transform files are constructed with rows which fit into a 1024 byte row
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001771 * buffer. This makes allocation easier below. Further regardless of the file
John Bowler660c6e42010-12-19 06:22:23 -06001772 * format every row has 128 pixels (giving 1024 bytes for 64bpp formats).
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001773 *
1774 * Files are stored with no gAMA or sBIT chunks, with a PLTE only when needed
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001775 * and with an ID derived from the colour type, bit depth and interlace type
John Bowler660c6e42010-12-19 06:22:23 -06001776 * as above (FILEID). The width (128) and height (variable) are not stored in
1777 * the FILEID - instead the fields are set to 0, indicating a transform file.
1778 *
1779 * The size files ar constructed with rows a maximum of 128 bytes wide, allowing
1780 * a maximum width of 16 pixels (for the 64bpp case.) They also have a maximum
1781 * height of 16 rows. The width and height are stored in the FILEID and, being
1782 * non-zero, indicate a size file.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001783 */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001784
John Bowler660c6e42010-12-19 06:22:23 -06001785/* The number of passes is related to the interlace type. There wass no libpng
1786 * API to determine this prior to 1.5, so we need an inquiry function:
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001787 */
1788static int
1789npasses_from_interlace_type(png_structp pp, int interlace_type)
1790{
1791 switch (interlace_type)
1792 {
1793 default:
1794 png_error(pp, "invalid interlace type");
1795
1796 case PNG_INTERLACE_NONE:
1797 return 1;
1798
1799 case PNG_INTERLACE_ADAM7:
John Bowler660c6e42010-12-19 06:22:23 -06001800 return PNG_INTERLACE_ADAM7_PASSES;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001801 }
1802}
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001803
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001804static unsigned int
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001805bit_size(png_structp pp, png_byte colour_type, png_byte bit_depth)
1806{
1807 switch (colour_type)
1808 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001809 case 0: return bit_depth;
1810
1811 case 2: return 3*bit_depth;
1812
1813 case 3: return bit_depth;
1814
1815 case 4: return 2*bit_depth;
1816
1817 case 6: return 4*bit_depth;
1818
1819 default: png_error(pp, "invalid color type");
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001820 }
1821}
1822
John Bowler660c6e42010-12-19 06:22:23 -06001823#define TRANSFORM_WIDTH 128U
1824#define TRANSFORM_ROWMAX (TRANSFORM_WIDTH*8U)
1825#define SIZE_ROWMAX (16*8U) /* 16 pixels, max 8 bytes each - 128 bytes */
1826#define STANDARD_ROWMAX TRANSFORM_ROWMAX /* The larger of the two */
1827
1828/* So the maximum image sizes are as follows. A 'transform' image may require
1829 * more than 65535 bytes. The size images are a maximum of 2046 bytes.
1830 */
1831#define TRANSFORM_IMAGEMAX (TRANSFORM_ROWMAX * (png_uint_32)2048)
1832#define SIZE_IMAGEMAX (SIZE_ROWMAX * 16U)
1833
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001834static size_t
John Bowler660c6e42010-12-19 06:22:23 -06001835transform_rowsize(png_structp pp, png_byte colour_type, png_byte bit_depth)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001836{
John Bowler660c6e42010-12-19 06:22:23 -06001837 return (TRANSFORM_WIDTH * bit_size(pp, colour_type, bit_depth)) / 8;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001838}
1839
John Bowler660c6e42010-12-19 06:22:23 -06001840/* transform_width(pp, colour_type, bit_depth) current returns the same number
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05001841 * every time, so just use a macro:
1842 */
John Bowler660c6e42010-12-19 06:22:23 -06001843#define transform_width(pp, colour_type, bit_depth) TRANSFORM_WIDTH
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001844
1845static png_uint_32
John Bowler660c6e42010-12-19 06:22:23 -06001846transform_height(png_structp pp, png_byte colour_type, png_byte bit_depth)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001847{
1848 switch (bit_size(pp, colour_type, bit_depth))
1849 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001850 case 1:
1851 case 2:
1852 case 4:
1853 return 1; /* Total of 128 pixels */
1854
1855 case 8:
1856 return 2; /* Total of 256 pixels/bytes */
1857
1858 case 16:
1859 return 512; /* Total of 65536 pixels */
1860
1861 case 24:
1862 case 32:
1863 return 512; /* 65536 pixels */
1864
1865 case 48:
1866 case 64:
1867 return 2048;/* 4 x 65536 pixels. */
1868
1869 default:
1870 return 0; /* Error, will be caught later */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001871 }
1872}
1873
John Bowler660c6e42010-12-19 06:22:23 -06001874/* The following can only be defined here, now we have the definitions
1875 * of the transform image sizes.
1876 */
1877static png_uint_32
1878standard_width(png_structp pp, png_uint_32 id)
1879{
1880 png_uint_32 width = WIDTH_FROM_ID(id);
1881 UNUSED(pp);
1882
1883 if (width == 0)
1884 width = transform_width(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
1885
1886 return width;
1887}
1888
1889static png_uint_32
1890standard_height(png_structp pp, png_uint_32 id)
1891{
1892 png_uint_32 height = HEIGHT_FROM_ID(id);
1893
1894 if (height == 0)
1895 height = transform_height(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
1896
1897 return height;
1898}
1899
1900static png_uint_32
1901standard_rowsize(png_structp pp, png_uint_32 id)
1902{
1903 png_uint_32 width = standard_width(pp, id);
1904
1905 /* This won't overflow: */
1906 width *= bit_size(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
1907 return (width + 7) / 8;
1908}
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05001909
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001910static void
John Bowler660c6e42010-12-19 06:22:23 -06001911transform_row(png_structp pp, png_byte buffer[TRANSFORM_ROWMAX],
1912 png_byte colour_type, png_byte bit_depth, png_uint_32 y)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05001913{
1914 png_uint_32 v = y << 7;
1915 png_uint_32 i = 0;
1916
1917 switch (bit_size(pp, colour_type, bit_depth))
1918 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001919 case 1:
1920 while (i<128/8) buffer[i] = v & 0xff, v += 17, ++i;
1921 return;
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05001922
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001923 case 2:
1924 while (i<128/4) buffer[i] = v & 0xff, v += 33, ++i;
1925 return;
1926
1927 case 4:
1928 while (i<128/2) buffer[i] = v & 0xff, v += 65, ++i;
1929 return;
1930
1931 case 8:
1932 /* 256 bytes total, 128 bytes in each row set as follows: */
1933 while (i<128) buffer[i] = v & 0xff, ++v, ++i;
1934 return;
1935
1936 case 16:
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06001937 /* Generate all 65536 pixel values in order, which includes the 8 bit
1938 * GA case as well as the 16 bit G case.
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05001939 */
1940 while (i<128)
1941 buffer[2*i] = (v>>8) & 0xff, buffer[2*i+1] = v & 0xff, ++v, ++i;
1942
1943 return;
1944
1945 case 24:
1946 /* 65535 pixels, but rotate the values. */
1947 while (i<128)
1948 {
1949 /* Three bytes per pixel, r, g, b, make b by r^g */
1950 buffer[3*i+0] = (v >> 8) & 0xff;
1951 buffer[3*i+1] = v & 0xff;
1952 buffer[3*i+2] = ((v >> 8) ^ v) & 0xff;
1953 ++v;
1954 ++i;
1955 }
1956
1957 return;
1958
1959 case 32:
1960 /* 65535 pixels, r, g, b, a; just replicate */
1961 while (i<128)
1962 {
1963 buffer[4*i+0] = (v >> 8) & 0xff;
1964 buffer[4*i+1] = v & 0xff;
1965 buffer[4*i+2] = (v >> 8) & 0xff;
1966 buffer[4*i+3] = v & 0xff;
1967 ++v;
1968 ++i;
1969 }
1970
1971 return;
1972
1973 case 48:
1974 /* y is maximum 2047, giving 4x65536 pixels, make 'r' increase by 1 at
1975 * each pixel, g increase by 257 (0x101) and 'b' by 0x1111:
1976 */
1977 while (i<128)
1978 {
1979 png_uint_32 t = v++;
1980 buffer[6*i+0] = (t >> 8) & 0xff;
1981 buffer[6*i+1] = t & 0xff;
1982 t *= 257;
1983 buffer[6*i+2] = (t >> 8) & 0xff;
1984 buffer[6*i+3] = t & 0xff;
1985 t *= 17;
1986 buffer[6*i+4] = (t >> 8) & 0xff;
1987 buffer[6*i+5] = t & 0xff;
1988 ++i;
1989 }
1990
1991 return;
1992
1993 case 64:
1994 /* As above in the 32 bit case. */
1995 while (i<128)
1996 {
1997 png_uint_32 t = v++;
1998 buffer[8*i+0] = (t >> 8) & 0xff;
1999 buffer[8*i+1] = t & 0xff;
2000 buffer[8*i+4] = (t >> 8) & 0xff;
2001 buffer[8*i+5] = t & 0xff;
2002 t *= 257;
2003 buffer[8*i+2] = (t >> 8) & 0xff;
2004 buffer[8*i+3] = t & 0xff;
2005 buffer[8*i+6] = (t >> 8) & 0xff;
2006 buffer[8*i+7] = t & 0xff;
2007 ++i;
2008 }
2009 return;
2010
2011 default:
2012 break;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002013 }
2014
2015 png_error(pp, "internal error");
2016}
2017
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002018/* This is just to do the right cast - could be changed to a function to check
2019 * 'bd' but there isn't much point.
2020 */
2021#define DEPTH(bd) ((png_byte)(1U << (bd)))
2022
John Bowler660c6e42010-12-19 06:22:23 -06002023/* Make a standardized image given a an image colour type, bit depth and
2024 * interlace type. The standard images have a very restricted range of
2025 * rows and heights and are used for testing transforms rather than image
2026 * layout details. See make_size_images below for a way to make images
2027 * that test odd sizes along with the libpng interlace handling.
2028 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002029static void
John Bowler660c6e42010-12-19 06:22:23 -06002030make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002031 png_byte PNG_CONST bit_depth, int interlace_type, png_const_charp name)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002032{
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002033 context(ps, fault);
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05002034
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002035 Try
2036 {
2037 png_infop pi;
2038 png_structp pp = set_store_for_write(ps, &pi, name);
2039 png_uint_32 h;
2040
2041 /* In the event of a problem return control to the Catch statement below
2042 * to do the clean up - it is not possible to 'return' directly from a Try
2043 * block.
2044 */
2045 if (pp == NULL)
2046 Throw ps;
2047
John Bowler660c6e42010-12-19 06:22:23 -06002048 h = transform_height(pp, colour_type, bit_depth);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002049
John Bowler660c6e42010-12-19 06:22:23 -06002050 png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth), h,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002051 bit_depth, colour_type, interlace_type,
2052 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
2053
2054 if (colour_type == 3) /* palette */
2055 {
2056 unsigned int i = 0;
2057 png_color pal[256];
2058
2059 do
2060 pal[i].red = pal[i].green = pal[i].blue = (png_byte)i;
2061 while(++i < 256U);
2062
2063 png_set_PLTE(pp, pi, pal, 256);
2064 }
2065
2066 png_write_info(pp, pi);
2067
2068 if (png_get_rowbytes(pp, pi) !=
John Bowler660c6e42010-12-19 06:22:23 -06002069 transform_rowsize(pp, colour_type, bit_depth))
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002070 png_error(pp, "row size incorrect");
2071
2072 else
2073 {
2074 /* Somewhat confusingly this must be called *after* png_write_info
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002075 * because if it is called before, the information in *pp has not been
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002076 * updated to reflect the interlaced image.
2077 */
2078 int npasses = png_set_interlace_handling(pp);
2079 int pass;
2080
2081 if (npasses != npasses_from_interlace_type(pp, interlace_type))
2082 png_error(pp, "write: png_set_interlace_handling failed");
2083
John Bowler660c6e42010-12-19 06:22:23 -06002084 for (pass=0; pass<npasses; ++pass)
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002085 {
2086 png_uint_32 y;
2087
2088 for (y=0; y<h; ++y)
2089 {
John Bowler660c6e42010-12-19 06:22:23 -06002090 png_byte buffer[TRANSFORM_ROWMAX];
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002091
John Bowler660c6e42010-12-19 06:22:23 -06002092 transform_row(pp, buffer, colour_type, bit_depth, y);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002093 png_write_row(pp, buffer);
2094 }
2095 }
2096 }
2097
2098 png_write_end(pp, pi);
2099
2100 /* And store this under the appropriate id, then clean up. */
John Bowler660c6e42010-12-19 06:22:23 -06002101 store_storefile(ps, FILEID(colour_type, bit_depth, interlace_type,
2102 0, 0, 0));
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002103
2104 store_write_reset(ps);
2105 }
2106
2107 Catch(fault)
2108 {
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002109 /* Use the png_store returned by the exception. This may help the compiler
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05002110 * because 'ps' is not used in this branch of the setjmp. Note that fault
2111 * and ps will always be the same value.
2112 */
2113 store_write_reset(fault);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002114 }
2115}
2116
2117static void
2118make_standard(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo,
2119 int PNG_CONST bdhi)
2120{
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002121 for (; bdlo <= bdhi; ++bdlo)
2122 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002123 int interlace_type;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002124
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002125 for (interlace_type = PNG_INTERLACE_NONE;
2126 interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002127 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002128 char name[FILE_NAME_SIZE];
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002129
John Bowler660c6e42010-12-19 06:22:23 -06002130 standard_name(name, sizeof name, 0, colour_type, bdlo, interlace_type,
2131 0, 0, 0);
2132 make_transform_image(ps, colour_type, DEPTH(bdlo), interlace_type,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002133 name);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002134 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002135 }
2136}
2137
2138static void
John Bowler660c6e42010-12-19 06:22:23 -06002139make_transform_images(png_store *ps)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002140{
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002141 /* This is in case of errors. */
2142 safecat(ps->test, sizeof ps->test, 0, "make standard images");
2143
2144 /* Arguments are colour_type, low bit depth, high bit depth
2145 */
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05002146 make_standard(ps, 0, 0, WRITE_BDHI);
2147 make_standard(ps, 2, 3, WRITE_BDHI);
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06002148 make_standard(ps, 3, 0, 3 /*palette: max 8 bits*/);
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05002149 make_standard(ps, 4, 3, WRITE_BDHI);
2150 make_standard(ps, 6, 3, WRITE_BDHI);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002151}
2152
John Bowler660c6e42010-12-19 06:22:23 -06002153/* The following two routines use the PNG interlace support macros from
2154 * png.h to interlace or deinterlace rows.
2155 */
2156static void
2157interlace_row(png_bytep buffer, png_const_bytep imageRow,
2158 unsigned int pixel_size, png_uint_32 w, int pass)
2159{
2160 png_uint_32 xin, xout, xstep;
2161
2162 /* Note that this can, trivially, be optimized to a memcpy on pass 7, the
2163 * code is presented this way to make it easier to understand. In practice
2164 * consult the code in the libpng source to see other ways of doing this.
2165 */
2166 xin = PNG_PASS_START_COL(pass);
2167 xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
2168
2169 for (xout=0; xin<w; xin+=xstep)
2170 {
2171 pixel_copy(buffer, xout, imageRow, xin, pixel_size);
2172 ++xout;
2173 }
2174}
2175
2176static void
2177deinterlace_row(png_bytep buffer, png_const_bytep row,
2178 unsigned int pixel_size, png_uint_32 w, int pass)
2179{
2180 /* The inverse of the above, 'row' is part of row 'y' of the output image,
2181 * in 'buffer'. The image is 'w' wide and this is pass 'pass', distribute
2182 * the pixels of row into buffer and return the number written (to allow
2183 * this to be checked).
2184 */
2185 png_uint_32 xin, xout, xstep;
2186
2187 xout = PNG_PASS_START_COL(pass);
2188 xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
2189
2190 for (xin=0; xout<w; xout+=xstep)
2191 {
2192 pixel_copy(buffer, xout, row, xin, pixel_size);
2193 ++xin;
2194 }
2195}
2196
2197/* Build a single row for the 'size' test images, this fills in only the
2198 * first bit_width bits of the sample row.
2199 */
2200static void
2201size_row(png_byte buffer[SIZE_ROWMAX], png_uint_32 bit_width, png_uint_32 y)
2202{
2203 /* height is in the range 1 to 16, so: */
2204 y = ((y & 1) << 7) + ((y & 2) << 6) + ((y & 4) << 5) + ((y & 8) << 4);
2205 /* the following ensures bits are set in small images: */
2206 y ^= 0xA5;
2207
2208 while (bit_width >= 8)
2209 *buffer++ = (png_byte)y++, bit_width -= 8;
2210
2211 /* There may be up to 7 remaining bits, these go in the most significant
2212 * bits of the byte.
2213 */
2214 if (bit_width > 0)
2215 {
2216 png_uint_32 mask = (1U<<(8-bit_width))-1;
2217 *buffer = (png_byte)((*buffer & mask) | (y & ~mask));
2218 }
2219}
2220
2221static void
2222make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
2223 png_byte PNG_CONST bit_depth, int PNG_CONST interlace_type,
2224 png_uint_32 PNG_CONST w, png_uint_32 PNG_CONST h,
2225 int PNG_CONST do_interlace)
2226{
John Bowler660c6e42010-12-19 06:22:23 -06002227 context(ps, fault);
2228
John Bowler660c6e42010-12-19 06:22:23 -06002229 Try
2230 {
2231 png_infop pi;
John Bowler56a739b2010-12-19 16:33:20 -06002232 png_structp pp;
John Bowler660c6e42010-12-19 06:22:23 -06002233 unsigned int pixel_size;
John Bowler56a739b2010-12-19 16:33:20 -06002234
2235 /* Make a name and get an appropriate id for the store: */
2236 char name[FILE_NAME_SIZE];
2237 PNG_CONST png_uint_32 id = FILEID(colour_type, bit_depth, interlace_type,
2238 w, h, do_interlace);
2239
2240 standard_name_from_id(name, sizeof name, 0, id);
2241 pp = set_store_for_write(ps, &pi, name);
John Bowler660c6e42010-12-19 06:22:23 -06002242
2243 /* In the event of a problem return control to the Catch statement below
2244 * to do the clean up - it is not possible to 'return' directly from a Try
2245 * block.
2246 */
2247 if (pp == NULL)
2248 Throw ps;
2249
2250 png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
2251 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
2252
2253 /* Same palette as make_transform_image - I don' think there is any
2254 * benefit from using a different one (JB 20101211)
2255 */
2256 if (colour_type == 3) /* palette */
2257 {
2258 unsigned int i = 0;
2259 png_color pal[256];
2260
2261 do
2262 pal[i].red = pal[i].green = pal[i].blue = (png_byte)i;
2263 while(++i < 256U);
2264
2265 png_set_PLTE(pp, pi, pal, 256);
2266 }
2267
2268 png_write_info(pp, pi);
2269
2270 /* Calculate the bit size, divide by 8 to get the byte size - this won't
2271 * overflow because we know the w values are all small enough even for
2272 * a system where 'unsigned int' is only 16 bits.
2273 */
2274 pixel_size = bit_size(pp, colour_type, bit_depth);
2275 if (png_get_rowbytes(pp, pi) != ((w * pixel_size) + 7) / 8)
2276 png_error(pp, "row size incorrect");
2277
2278 else
2279 {
2280 int npasses = npasses_from_interlace_type(pp, interlace_type);
2281 png_uint_32 y;
2282 int pass;
2283 png_byte image[16][SIZE_ROWMAX];
2284
2285 /* To help consistent error detection make the parts of this buffer
2286 * that aren't set below all '1':
2287 */
2288 memset(image, 0xff, sizeof image);
2289
2290 if (!do_interlace && npasses != png_set_interlace_handling(pp))
2291 png_error(pp, "write: png_set_interlace_handling failed");
2292
2293 /* Prepare the whole image first to avoid making it 7 times: */
2294 for (y=0; y<h; ++y)
2295 size_row(image[y], w * pixel_size, y);
2296
2297 for (pass=0; pass<npasses; ++pass)
2298 {
2299 /* The following two are for checking the macros: */
2300 PNG_CONST png_uint_32 wPass = PNG_PASS_COLS(w, pass);
2301
2302 /* If do_interlace is set we don't call png_write_row for every
2303 * row because some of them are empty. In fact, for a 1x1 image,
2304 * most of them are empty!
2305 */
2306 for (y=0; y<h; ++y)
2307 {
2308 png_const_bytep row = image[y];
2309 png_byte tempRow[SIZE_ROWMAX];
2310
2311 /* If do_interlace *and* the image is interlaced we
2312 * need a reduced interlace row, this may be reduced
2313 * to empty.
2314 */
2315 if (do_interlace && interlace_type == PNG_INTERLACE_ADAM7)
2316 {
2317 /* The row must not be written if it doesn't exist, notice
2318 * that there are two conditions here, either the row isn't
2319 * ever in the pass or the row would be but isn't wide
2320 * enough to contribute any pixels. In fact the wPass test
2321 * can be used to skip the whole y loop in this case.
2322 */
2323 if (PNG_ROW_IN_INTERLACE_PASS(y, pass) && wPass > 0)
2324 {
2325 /* Set to all 1's for error detection (libpng tends to
2326 * set unset things to 0).
2327 */
2328 memset(tempRow, 0xff, sizeof tempRow);
2329 interlace_row(tempRow, row, pixel_size, w, pass);
2330 row = tempRow;
2331 }
2332 else
2333 continue;
2334 }
2335
2336 /* Only get to here if the row has some pixels in it. */
2337 png_write_row(pp, row);
2338 }
2339 }
2340 }
2341
2342 png_write_end(pp, pi);
2343
2344 /* And store this under the appropriate id, then clean up. */
2345 store_storefile(ps, id);
2346
2347 store_write_reset(ps);
2348 }
2349
2350 Catch(fault)
2351 {
2352 /* Use the png_store returned by the exception. This may help the compiler
2353 * because 'ps' is not used in this branch of the setjmp. Note that fault
2354 * and ps will always be the same value.
2355 */
2356 store_write_reset(fault);
2357 }
2358}
2359
2360static void
2361make_size(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo,
2362 int PNG_CONST bdhi)
2363{
2364 for (; bdlo <= bdhi; ++bdlo)
2365 {
2366 png_uint_32 width;
2367
2368 for (width = 1; width <= 16; ++width)
2369 {
2370 png_uint_32 height;
2371
2372 for (height = 1; height <= 16; ++height)
2373 {
2374 /* The four combinations of DIY interlace and interlace or not -
2375 * no interlace + DIY should be identical to no interlace with
2376 * libpng doing it.
2377 */
2378 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
2379 width, height, 0);
2380 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
2381 width, height, 1);
2382 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
2383 width, height, 0);
2384 make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
2385 width, height, 1);
2386 }
2387 }
2388 }
2389}
2390
2391static void
2392make_size_images(png_store *ps)
2393{
2394 /* This is in case of errors. */
2395 safecat(ps->test, sizeof ps->test, 0, "make size images");
2396
2397 /* Arguments are colour_type, low bit depth, high bit depth
2398 */
2399 make_size(ps, 0, 0, WRITE_BDHI);
2400 make_size(ps, 2, 3, WRITE_BDHI);
2401 make_size(ps, 3, 0, 3 /*palette: max 8 bits*/);
2402 make_size(ps, 4, 3, WRITE_BDHI);
2403 make_size(ps, 6, 3, WRITE_BDHI);
2404}
2405
2406/* Return a row based on image id and 'y' for checking: */
2407static void
2408standard_row(png_structp pp, png_byte std[STANDARD_ROWMAX], png_uint_32 id,
2409 png_uint_32 y)
2410{
2411 if (WIDTH_FROM_ID(id) == 0)
2412 transform_row(pp, std, COL_FROM_ID(id), DEPTH_FROM_ID(id), y);
2413 else
2414 size_row(std, WIDTH_FROM_ID(id) * bit_size(pp, COL_FROM_ID(id),
2415 DEPTH_FROM_ID(id)), y);
2416}
2417
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002418/* Tests - individual test cases */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002419/* Like 'make_standard' but errors are deliberately introduced into the calls
2420 * to ensure that they get detected - it should not be possible to write an
2421 * invalid image with libpng!
2422 */
2423static void
2424sBIT0_error_fn(png_structp pp, png_infop pi)
2425{
2426 /* 0 is invalid... */
2427 png_color_8 bad;
2428 bad.red = bad.green = bad.blue = bad.gray = bad.alpha = 0;
2429 png_set_sBIT(pp, pi, &bad);
2430}
2431
2432static void
2433sBIT_error_fn(png_structp pp, png_infop pi)
2434{
2435 png_byte bit_depth;
2436 png_color_8 bad;
2437
2438 if (png_get_color_type(pp, pi) == PNG_COLOR_TYPE_PALETTE)
2439 bit_depth = 8;
2440
2441 else
2442 bit_depth = png_get_bit_depth(pp, pi);
2443
2444 /* Now we know the bit depth we can easily generate an invalid sBIT entry */
2445 bad.red = bad.green = bad.blue = bad.gray = bad.alpha =
2446 (png_byte)(bit_depth+1);
2447 png_set_sBIT(pp, pi, &bad);
2448}
2449
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002450static PNG_CONST struct
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002451{
2452 void (*fn)(png_structp, png_infop);
2453 PNG_CONST char *msg;
2454 unsigned int warning :1; /* the error is a warning... */
2455} error_test[] =
2456 {
2457 { sBIT0_error_fn, "sBIT(0): failed to detect error", 1 },
2458 { sBIT_error_fn, "sBIT(too big): failed to detect error", 1 },
2459 };
2460
2461static void
John Bowler56a739b2010-12-19 16:33:20 -06002462make_error(png_store* volatile ps, png_byte PNG_CONST colour_type,
2463 png_byte bit_depth, int interlace_type, int test, png_const_charp name)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002464{
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002465 context(ps, fault);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002466
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002467 Try
2468 {
2469 png_structp pp;
2470 png_infop pi;
2471
2472 pp = set_store_for_write(ps, &pi, name);
2473
2474 if (pp == NULL)
2475 Throw ps;
2476
John Bowler660c6e42010-12-19 06:22:23 -06002477 png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth),
2478 transform_height(pp, colour_type, bit_depth), bit_depth, colour_type,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002479 interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
2480
2481 if (colour_type == 3) /* palette */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002482 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002483 unsigned int i = 0;
2484 png_color pal[256];
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002485
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002486 do
2487 pal[i].red = pal[i].green = pal[i].blue = (png_byte)i;
2488 while(++i < 256U);
2489
2490 png_set_PLTE(pp, pi, pal, 256);
2491 }
2492
2493 /* Time for a few errors, these are in various optional chunks, the
2494 * standard tests test the standard chunks pretty well.
2495 */
John Bowler168a4332011-01-16 19:32:22 -06002496# define exception__prev exception_prev_1
2497# define exception__env exception_env_1
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002498 Try
2499 {
2500 /* Expect this to throw: */
2501 ps->expect_error = !error_test[test].warning;
2502 ps->expect_warning = error_test[test].warning;
2503 ps->saw_warning = 0;
2504 error_test[test].fn(pp, pi);
2505
2506 /* Normally the error is only detected here: */
2507 png_write_info(pp, pi);
2508
2509 /* And handle the case where it was only a warning: */
2510 if (ps->expect_warning && ps->saw_warning)
2511 Throw ps;
2512
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05002513 /* If we get here there is a problem, we have success - no error or
2514 * no warning - when we shouldn't have success. Log an error.
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002515 */
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06002516 store_log(ps, pp, error_test[test].msg, 1 /*error*/);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002517 }
2518
2519 Catch (fault)
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05002520 ps = fault; /* expected exit, make sure ps is not clobbered */
John Bowler168a4332011-01-16 19:32:22 -06002521#undef exception__prev
2522#undef exception__env
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002523
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05002524 /* And clear these flags */
2525 ps->expect_error = 0;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002526 ps->expect_warning = 0;
2527
2528 /* Now write the whole image, just to make sure that the detected, or
2529 * undetected, errro has not created problems inside libpng.
2530 */
2531 if (png_get_rowbytes(pp, pi) !=
John Bowler660c6e42010-12-19 06:22:23 -06002532 transform_rowsize(pp, colour_type, bit_depth))
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002533 png_error(pp, "row size incorrect");
2534
2535 else
2536 {
John Bowler660c6e42010-12-19 06:22:23 -06002537 png_uint_32 h = transform_height(pp, colour_type, bit_depth);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002538 int npasses = png_set_interlace_handling(pp);
2539 int pass;
2540
2541 if (npasses != npasses_from_interlace_type(pp, interlace_type))
2542 png_error(pp, "write: png_set_interlace_handling failed");
2543
John Bowler660c6e42010-12-19 06:22:23 -06002544 for (pass=0; pass<npasses; ++pass)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002545 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002546 png_uint_32 y;
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06002547
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002548 for (y=0; y<h; ++y)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002549 {
John Bowler660c6e42010-12-19 06:22:23 -06002550 png_byte buffer[TRANSFORM_ROWMAX];
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002551
John Bowler660c6e42010-12-19 06:22:23 -06002552 transform_row(pp, buffer, colour_type, bit_depth, y);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002553 png_write_row(pp, buffer);
2554 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002555 }
2556 }
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002557
2558 png_write_end(pp, pi);
2559
2560 /* The following deletes the file that was just written. */
2561 store_write_reset(ps);
2562 }
2563
2564 Catch(fault)
2565 {
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05002566 store_write_reset(fault);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002567 }
2568}
2569
2570static int
2571make_errors(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
2572 int bdlo, int PNG_CONST bdhi)
2573{
2574 for (; bdlo <= bdhi; ++bdlo)
2575 {
2576 int interlace_type;
2577
2578 for (interlace_type = PNG_INTERLACE_NONE;
2579 interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
2580 {
2581 unsigned int test;
2582 char name[FILE_NAME_SIZE];
2583
John Bowler660c6e42010-12-19 06:22:23 -06002584 standard_name(name, sizeof name, 0, colour_type, bdlo, interlace_type,
2585 0, 0, 0);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002586
2587 for (test=0; test<(sizeof error_test)/(sizeof error_test[0]); ++test)
2588 {
2589 make_error(&pm->this, colour_type, DEPTH(bdlo), interlace_type,
2590 test, name);
2591
2592 if (fail(pm))
2593 return 0;
2594 }
2595 }
2596 }
2597
2598 return 1; /* keep going */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002599}
2600
2601static void
2602perform_error_test(png_modifier *pm)
2603{
2604 /* Need to do this here because we just write in this test. */
2605 safecat(pm->this.test, sizeof pm->this.test, 0, "error test");
2606
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05002607 if (!make_errors(pm, 0, 0, WRITE_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002608 return;
2609
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05002610 if (!make_errors(pm, 2, 3, WRITE_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002611 return;
2612
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002613 if (!make_errors(pm, 3, 0, 3))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002614 return;
2615
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05002616 if (!make_errors(pm, 4, 3, WRITE_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002617 return;
2618
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05002619 if (!make_errors(pm, 6, 3, WRITE_BDHI))
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05002620 return;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05002621}
2622
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002623/* Because we want to use the same code in both the progressive reader and the
2624 * sequential reader it is necessary to deal with the fact that the progressive
2625 * reader callbacks only have one parameter (png_get_progressive_ptr()), so this
2626 * must contain all the test parameters and all the local variables directly
2627 * accessible to the sequential reader implementation.
2628 *
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002629 * The technique adopted is to reinvent part of what Dijkstra termed a
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002630 * 'display'; an array of pointers to the stack frames of enclosing functions so
2631 * that a nested function definition can access the local (C auto) variables of
2632 * the functions that contain its definition. In fact C provides the first
2633 * pointer (the local variables - the stack frame pointer) and the last (the
2634 * global variables - the BCPL global vector typically implemented as global
2635 * addresses), this code requires one more pointer to make the display - the
2636 * local variables (and function call parameters) of the function that actually
2637 * invokes either the progressive or sequential reader.
2638 *
2639 * Perhaps confusingly this technique is confounded with classes - the
2640 * 'standard_display' defined here is sub-classed as the 'gamma_display' below.
2641 * A gamma_display is a standard_display, taking advantage of the ANSI-C
2642 * requirement that the pointer to the first member of a structure must be the
2643 * same as the pointer to the structure. This allows us to reuse standard_
2644 * functions in the gamma test code; something that could not be done with
2645 * nested funtions!
2646 */
2647typedef struct standard_display
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002648{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002649 png_store* ps; /* Test parameters (passed to the function) */
2650 png_byte colour_type;
2651 png_byte bit_depth;
2652 int interlace_type;
2653 png_uint_32 id; /* Calculated file ID */
2654 png_uint_32 w; /* Width of image */
2655 png_uint_32 h; /* Height of image */
2656 int npasses; /* Number of interlaced passes */
John Bowler660c6e42010-12-19 06:22:23 -06002657 png_uint_32 pixel_size; /* Width of one pixel in bits */
2658 png_uint_32 bit_width; /* Width of output row in bits */
2659 size_t cbRow; /* Bytes in a row of the output image */
2660 int do_interlace; /* Do interlacing internally */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002661} standard_display;
2662
2663static void
John Bowler660c6e42010-12-19 06:22:23 -06002664standard_display_init(standard_display *dp, png_store* ps, png_uint_32 id,
2665 int do_interlace)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002666{
2667 dp->ps = ps;
John Bowler660c6e42010-12-19 06:22:23 -06002668 dp->colour_type = COL_FROM_ID(id);
2669 dp->bit_depth = DEPTH_FROM_ID(id);
2670 dp->interlace_type = INTERLACE_FROM_ID(id);
2671 dp->id = id;
2672 /* All the rest are filled in after the read_info: */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002673 dp->w = 0;
2674 dp->h = 0;
2675 dp->npasses = 0;
John Bowler660c6e42010-12-19 06:22:23 -06002676 dp->pixel_size = 0;
2677 dp->bit_width = 0;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002678 dp->cbRow = 0;
John Bowler660c6e42010-12-19 06:22:23 -06002679 dp->do_interlace = do_interlace;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002680}
2681
2682/* By passing a 'standard_display' the progressive callbacks can be used
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002683 * directly by the sequential code, the functions suffixed "_imp" are the
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002684 * implementations, the functions without the suffix are the callbacks.
2685 *
2686 * The code for the info callback is split into two because this callback calls
2687 * png_read_update_info or png_start_read_image and what gets called depends on
2688 * whether the info needs updating (we want to test both calls in pngvalid.)
2689 */
2690static void
2691standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
2692{
2693 if (png_get_bit_depth(pp, pi) != dp->bit_depth)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002694 png_error(pp, "validate: bit depth changed");
2695
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002696 if (png_get_color_type(pp, pi) != dp->colour_type)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002697 png_error(pp, "validate: color type changed");
2698
2699 if (png_get_filter_type(pp, pi) != PNG_FILTER_TYPE_BASE)
2700 png_error(pp, "validate: filter type changed");
2701
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002702 if (png_get_interlace_type(pp, pi) != dp->interlace_type)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002703 png_error(pp, "validate: interlacing changed");
2704
2705 if (png_get_compression_type(pp, pi) != PNG_COMPRESSION_TYPE_BASE)
2706 png_error(pp, "validate: compression type changed");
2707
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002708 dp->w = png_get_image_width(pp, pi);
2709
John Bowler660c6e42010-12-19 06:22:23 -06002710 if (dp->w != standard_width(pp, dp->id))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002711 png_error(pp, "validate: image width changed");
2712
2713 dp->h = png_get_image_height(pp, pi);
2714
John Bowler660c6e42010-12-19 06:22:23 -06002715 if (dp->h != standard_height(pp, dp->id))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002716 png_error(pp, "validate: image height changed");
2717
2718 /* Important: this is validating the value *before* any transforms have been
2719 * put in place. It doesn't matter for the standard tests, where there are
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002720 * no transforms, but it does for other tests where rowbytes may change after
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002721 * png_read_update_info.
2722 */
John Bowler660c6e42010-12-19 06:22:23 -06002723 if (png_get_rowbytes(pp, pi) != standard_rowsize(pp, dp->id))
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002724 png_error(pp, "validate: row size changed");
2725
2726 if (dp->colour_type == 3) /* palette */
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002727 {
2728 png_colorp pal;
2729 int num;
2730
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002731 /* This could be passed in but isn't - the values set above when the
2732 * standard images were made are just repeated here.
2733 */
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002734 if (png_get_PLTE(pp, pi, &pal, &num) & PNG_INFO_PLTE)
2735 {
2736 int i;
2737
2738 if (num != 256)
2739 png_error(pp, "validate: color type 3 PLTE chunk size changed");
2740
2741 for (i=0; i<num; ++i)
2742 if (pal[i].red != i || pal[i].green != i || pal[i].blue != i)
2743 png_error(pp, "validate: color type 3 PLTE chunk changed");
2744 }
2745
2746 else
2747 png_error(pp, "validate: missing PLTE with color type 3");
2748 }
2749
2750 /* Read the number of passes - expected to match the value used when
2751 * creating the image (interlaced or not). This has the side effect of
John Bowler660c6e42010-12-19 06:22:23 -06002752 * turning on interlace handling (if do_interlace is not set.)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002753 */
John Bowler660c6e42010-12-19 06:22:23 -06002754 dp->npasses = npasses_from_interlace_type(pp, dp->interlace_type);
2755 if (!dp->do_interlace && dp->npasses != png_set_interlace_handling(pp))
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002756 png_error(pp, "validate: file changed interlace type");
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002757
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002758 /* Caller calls png_read_update_info or png_start_read_image now, then calls
2759 * part2.
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002760 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002761}
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002762
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002763/* This must be called *after* the png_read_update_info call to get the correct
2764 * 'rowbytes' value, otherwise png_get_rowbytes will refer to the untransformed
2765 * image.
2766 */
2767static void
2768standard_info_part2(standard_display *dp, png_structp pp, png_infop pi,
2769 int nImages)
2770{
2771 /* Record cbRow now that it can be found. */
John Bowler660c6e42010-12-19 06:22:23 -06002772 dp->pixel_size = bit_size(pp, png_get_color_type(pp, pi),
2773 png_get_bit_depth(pp, pi));
2774 dp->bit_width = png_get_image_width(pp, pi) * dp->pixel_size;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002775 dp->cbRow = png_get_rowbytes(pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002776
John Bowler660c6e42010-12-19 06:22:23 -06002777 /* Validate the rowbytes here again. */
2778 if (dp->cbRow != (dp->bit_width+7)/8)
2779 png_error(pp, "bad png_get_rowbytes calculation");
2780
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002781 /* Then ensure there is enough space for the output image(s). */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002782 store_ensure_image(dp->ps, pp, nImages * dp->cbRow * dp->h);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002783}
2784
2785static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002786standard_info_imp(standard_display *dp, png_structp pp, png_infop pi,
2787 int nImages)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002788{
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002789 /* Note that the validation routine has the side effect of turning on
2790 * interlace handling in the subsequent code.
2791 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002792 standard_info_part1(dp, pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002793
2794 /* And the info callback has to call this (or png_read_update_info - see
2795 * below in the png_modifier code for that variant.
2796 */
2797 png_start_read_image(pp);
2798
2799 /* Validate the height, width and rowbytes plus ensure that sufficient buffer
2800 * exists for decoding the image.
2801 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002802 standard_info_part2(dp, pp, pi, nImages);
2803}
2804
2805static void
2806standard_info(png_structp pp, png_infop pi)
2807{
2808 standard_display *dp = png_get_progressive_ptr(pp);
2809
2810 /* Call with nImages==1 because the progressive reader can only produce one
2811 * image.
2812 */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06002813 standard_info_imp(dp, pp, pi, 1 /*only one image*/);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002814}
2815
2816static void
2817progressive_row(png_structp pp, png_bytep new_row, png_uint_32 y, int pass)
2818{
John Bowler660c6e42010-12-19 06:22:23 -06002819 PNG_CONST standard_display *dp = png_get_progressive_ptr(pp);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002820
2821 /* When handling interlacing some rows will be absent in each pass, the
John Bowler660c6e42010-12-19 06:22:23 -06002822 * callback still gets called, but with a NULL pointer. This is checked
2823 * in the 'else' clause below. We need our own 'cbRow', but we can't call
2824 * png_get_rowbytes because we got no info structure.
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002825 */
2826 if (new_row != NULL)
2827 {
John Bowler660c6e42010-12-19 06:22:23 -06002828 png_bytep row;
2829
2830 /* In the case where the reader doesn't do the interlace it gives
2831 * us the y in the sub-image:
2832 */
2833 if (dp->do_interlace && dp->interlace_type == PNG_INTERLACE_ADAM7)
2834 y = PNG_ROW_FROM_PASS_ROW(y, pass);
2835
2836 /* Validate this just in case. */
2837 if (y >= dp->h)
2838 png_error(pp, "invalid y to progressive row callback");
2839
2840 row = dp->ps->image + y * dp->cbRow;
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002841
2842 /* Combine the new row into the old: */
John Bowler660c6e42010-12-19 06:22:23 -06002843 if (dp->do_interlace)
2844 {
2845 if (dp->interlace_type == PNG_INTERLACE_ADAM7)
2846 deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass);
2847 else
2848 memcpy(row, new_row, dp->cbRow);
2849 }
2850 else
2851 png_progressive_combine_row(pp, row, new_row);
2852 } else if (dp->interlace_type == PNG_INTERLACE_ADAM7 &&
2853 PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
2854 PNG_PASS_COLS(dp->w, pass) > 0)
2855 png_error(pp, "missing row in progressive de-interlacing");
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002856}
2857
2858static void
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05002859sequential_row(standard_display *dp, png_structp pp, png_infop pi,
2860 PNG_CONST png_bytep pImage, PNG_CONST png_bytep pDisplay)
2861{
2862 PNG_CONST int npasses = dp->npasses;
John Bowler660c6e42010-12-19 06:22:23 -06002863 PNG_CONST int do_interlace = dp->do_interlace &&
2864 dp->interlace_type == PNG_INTERLACE_ADAM7;
2865 PNG_CONST png_uint_32 height = standard_height(pp, dp->id);
2866 PNG_CONST png_uint_32 width = standard_width(pp, dp->id);
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05002867 PNG_CONST size_t cbRow = dp->cbRow;
2868 int pass;
2869
John Bowler660c6e42010-12-19 06:22:23 -06002870 for (pass=0; pass<npasses; ++pass)
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05002871 {
2872 png_uint_32 y;
John Bowler660c6e42010-12-19 06:22:23 -06002873 png_uint_32 wPass = PNG_PASS_COLS(width, pass);
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05002874 png_bytep pRow1 = pImage;
2875 png_bytep pRow2 = pDisplay;
2876
John Bowler660c6e42010-12-19 06:22:23 -06002877 for (y=0; y<height; ++y)
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05002878 {
John Bowler660c6e42010-12-19 06:22:23 -06002879 if (do_interlace)
2880 {
2881 /* wPass may be zero or this row may not be in this pass.
2882 * png_read_row must not be called in either case.
2883 */
2884 if (wPass > 0 && PNG_ROW_IN_INTERLACE_PASS(y, pass))
2885 {
2886 /* Read the row into a pair of temporary buffers, then do the
2887 * merge here into the output rows.
2888 */
2889 png_byte row[STANDARD_ROWMAX], display[STANDARD_ROWMAX];
2890
2891 /* The following aids (to some extent) error detection - we can
2892 * see where png_read_row wrote. Use opposite values in row and
2893 * display to make this easier.
2894 */
2895 memset(row, 0xff, sizeof row);
2896 memset(display, 0, sizeof display);
2897
2898 png_read_row(pp, row, display);
2899
2900 if (pRow1 != NULL)
2901 deinterlace_row(pRow1, row, dp->pixel_size, dp->w, pass);
2902
2903 if (pRow2 != NULL)
2904 deinterlace_row(pRow2, display, dp->pixel_size, dp->w, pass);
2905 }
2906 }
2907 else
2908 png_read_row(pp, pRow1, pRow2);
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05002909
2910 if (pRow1 != NULL)
2911 pRow1 += cbRow;
2912
2913 if (pRow2 != NULL)
2914 pRow2 += cbRow;
2915 }
2916 }
2917
2918 /* And finish the read operation (only really necessary if the caller wants
2919 * to find additional data in png_info from chunks after the last IDAT.)
2920 */
2921 png_read_end(pp, pi);
2922}
2923
2924static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002925standard_row_validate(standard_display *dp, png_structp pp, png_const_bytep row,
2926 png_const_bytep display, png_uint_32 y)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002927{
John Bowler660c6e42010-12-19 06:22:23 -06002928 png_byte std[STANDARD_ROWMAX];
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002929
John Bowler660c6e42010-12-19 06:22:23 -06002930 memset(std, 0xff, sizeof std);
2931 standard_row(pp, std, dp->id, y);
2932
2933 /* At the end both the 'row' and 'display' arrays should end up identical.
2934 * In earlier passes 'row' will be partially filled in, with only the pixels
2935 * that have been read so far, but 'display' will have those pixels
2936 * replicated to fill the unread pixels while reading an interlaced image.
2937 * The side effect inside the libpng sequential reader is that the 'row'
2938 * array retains the correct values for unwritten pixels within the row
2939 * bytes, while the 'display' array gets bits off the end of the image (in
2940 * the last byte) trashed. Unfortunately in the progressive reader the
2941 * row bytes are always trashed, so we always do a pixel_cmp here even though
2942 * a memcmp of all cbRow bytes will succeed for the sequential reader.
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002943 */
John Bowler660c6e42010-12-19 06:22:23 -06002944 if (row != NULL && pixel_cmp(std, row, dp->bit_width) != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002945 {
2946 char msg[64];
2947 sprintf(msg, "PNG image row %d changed", y);
2948 png_error(pp, msg);
2949 }
2950
John Bowler660c6e42010-12-19 06:22:23 -06002951 /* In this case use pixel_cmp because we need to compare a partial
2952 * byte at the end of the row if the row is not an exact multiple
2953 * of 8 bits wide.
2954 */
2955 if (display != NULL && pixel_cmp(std, display, dp->bit_width) != 0)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002956 {
2957 char msg[64];
2958 sprintf(msg, "display row %d changed", y);
2959 png_error(pp, msg);
2960 }
2961}
2962
2963static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002964standard_image_validate(standard_display *dp, png_structp pp,
2965 png_const_bytep pImage, png_const_bytep pDisplay)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002966{
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002967 png_uint_32 y;
2968
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002969 for (y=0; y<dp->h; ++y)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002970 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002971 standard_row_validate(dp, pp, pImage, pDisplay, y);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002972
2973 if (pImage != NULL)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002974 pImage += dp->cbRow;
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002975
2976 if (pDisplay != NULL)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002977 pDisplay += dp->cbRow;
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002978 }
2979
2980 /* This avoids false positives if the validation code is never called! */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002981 dp->ps->validated = 1;
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002982}
2983
2984static void
2985standard_end(png_structp pp, png_infop pi)
2986{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002987 standard_display *dp = png_get_progressive_ptr(pp);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002988
2989 UNUSED(pi);
2990
2991 /* Validate the image - progressive reading only produces one variant for
2992 * interlaced images.
2993 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05002994 standard_image_validate(dp, pp, dp->ps->image, NULL);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05002995}
2996
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05002997/* A single test run checking the standard image to ensure it is not damaged. */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05002998static void
John Bowler660c6e42010-12-19 06:22:23 -06002999standard_test(png_store* PNG_CONST psIn, png_uint_32 PNG_CONST id,
3000 int do_interlace)
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003001{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003002 standard_display d;
3003 context(psIn, fault);
3004
3005 /* Set up the display (stack frame) variables from the arguments to the
3006 * function and initialize the locals that are filled in later.
3007 */
John Bowler660c6e42010-12-19 06:22:23 -06003008 standard_display_init(&d, psIn, id, do_interlace);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003009
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003010 /* Everything is protected by a Try/Catch. The functions called also
3011 * typically have local Try/Catch blocks.
3012 */
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003013 Try
3014 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003015 png_structp pp;
3016 png_infop pi;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003017
John Bowler660c6e42010-12-19 06:22:23 -06003018 /* Get a png_struct for reading the image. This will throw an error if it
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05003019 * fails, so we don't need to check the result.
3020 */
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003021 pp = set_store_for_read(d.ps, &pi, d.id,
John Bowler660c6e42010-12-19 06:22:23 -06003022 d.do_interlace ? (d.ps->progressive ?
3023 "pngvalid progressive deinterlacer" :
3024 "pngvalid sequential deinterlacer") : (d.ps->progressive ?
3025 "progressive reader" : "sequential reader"));
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003026
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003027 /* Introduce the correct read function. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003028 if (d.ps->progressive)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003029 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003030 png_set_progressive_read_fn(pp, &d, standard_info, progressive_row,
3031 standard_end);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003032
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003033 /* Now feed data into the reader until we reach the end: */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003034 store_progressive_read(d.ps, pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003035 }
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003036 else
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003037 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003038 /* Note that this takes the store, not the display. */
3039 png_set_read_fn(pp, d.ps, store_read);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003040
3041 /* Check the header values: */
3042 png_read_info(pp, pi);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003043
3044 /* The code tests both versions of the images that the sequential
3045 * reader can produce.
3046 */
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06003047 standard_info_imp(&d, pp, pi, 2 /*images*/);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003048
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003049 /* Need the total bytes in the image below; we can't get to this point
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003050 * unless the PNG file values have been checked against the expected
3051 * values.
3052 */
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003053 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003054 PNG_CONST png_bytep pImage = d.ps->image;
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003055 PNG_CONST png_bytep pDisplay = pImage + d.cbRow * d.h;
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003056
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003057 sequential_row(&d, pp, pi, pImage, pDisplay);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003058
3059 /* After the last pass loop over the rows again to check that the
3060 * image is correct.
3061 */
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003062 standard_image_validate(&d, pp, pImage, pDisplay);
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003063 }
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003064 }
3065
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003066 /* Check for validation. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003067 if (!d.ps->validated)
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05003068 png_error(pp, "image read failed silently");
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003069
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003070 /* Successful completion. */
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003071 }
3072
3073 Catch(fault)
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003074 d.ps = fault; /* make sure this hasn't been clobbered. */
3075
3076 /* In either case clean up the store. */
3077 store_read_reset(d.ps);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003078}
3079
3080static int
3081test_standard(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003082 int bdlo, int PNG_CONST bdhi)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003083{
3084 for (; bdlo <= bdhi; ++bdlo)
3085 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003086 int interlace_type;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003087
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003088 for (interlace_type = PNG_INTERLACE_NONE;
3089 interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
3090 {
John Bowler660c6e42010-12-19 06:22:23 -06003091 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
3092 interlace_type, 0, 0, 0), 0/*do_interlace*/);
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003093
3094 if (fail(pm))
3095 return 0;
3096 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003097 }
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003098
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003099 return 1; /* keep going */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003100}
3101
3102static void
3103perform_standard_test(png_modifier *pm)
3104{
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003105 /* Test each colour type over the valid range of bit depths (expressed as
3106 * log2(bit_depth) in turn, stop as soon as any error is detected.
3107 */
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003108 if (!test_standard(pm, 0, 0, READ_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003109 return;
3110
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003111 if (!test_standard(pm, 2, 3, READ_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003112 return;
3113
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003114 if (!test_standard(pm, 3, 0, 3))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003115 return;
3116
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003117 if (!test_standard(pm, 4, 3, READ_BDHI))
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003118 return;
3119
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003120 if (!test_standard(pm, 6, 3, READ_BDHI))
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003121 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003122}
3123
3124
John Bowler660c6e42010-12-19 06:22:23 -06003125/********************************** SIZE TESTS ********************************/
3126static int
3127test_size(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
3128 int bdlo, int PNG_CONST bdhi)
3129{
3130 /* Run the tests on each combination.
3131 *
3132 * NOTE: on my 32 bit x86 each of the following blocks takes
3133 * a total of 3.5 seconds if done across every combo of bit depth
3134 * width and height. This is a waste of time in practice, hence the
3135 * hinc and winc stuff:
3136 */
3137 static PNG_CONST png_byte hinc[] = {1, 3, 11, 1, 5};
3138 static PNG_CONST png_byte winc[] = {1, 9, 5, 7, 1};
3139 for (; bdlo <= bdhi; ++bdlo)
3140 {
3141 png_uint_32 h, w;
3142
3143 for (h=1; h<=16; h+=hinc[bdlo]) for (w=1; w<=16; w+=winc[bdlo])
3144 {
3145 /* First test all the 'size' images against the sequential
3146 * reader using libpng to deinterlace (where required.) This
3147 * validates the write side of libpng. There are four possibilities
3148 * to validate.
3149 */
3150 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
3151 PNG_INTERLACE_NONE, w, h, 0), 0/*do_interlace*/);
3152
3153 if (fail(pm))
3154 return 0;
3155
3156 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
3157 PNG_INTERLACE_NONE, w, h, 1), 0/*do_interlace*/);
3158
3159 if (fail(pm))
3160 return 0;
3161
3162 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
3163 PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/);
3164
3165 if (fail(pm))
3166 return 0;
3167
3168 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
3169 PNG_INTERLACE_ADAM7, w, h, 1), 0/*do_interlace*/);
3170
3171 if (fail(pm))
3172 return 0;
3173
3174 /* Now validate the interlaced read side - do_interlace true,
3175 * in the progressive case this does actually make a difference
3176 * to the code used in the non-interlaced case too.
3177 */
3178 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
3179 PNG_INTERLACE_NONE, w, h, 0), 1/*do_interlace*/);
3180
3181 if (fail(pm))
3182 return 0;
3183
3184 standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
3185 PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/);
3186
3187 if (fail(pm))
3188 return 0;
3189 }
3190 }
3191
3192 return 1; /* keep going */
3193}
3194
3195static void
3196perform_size_test(png_modifier *pm)
3197{
3198 /* Test each colour type over the valid range of bit depths (expressed as
3199 * log2(bit_depth) in turn, stop as soon as any error is detected.
3200 */
3201 if (!test_size(pm, 0, 0, READ_BDHI))
3202 return;
3203
3204 if (!test_size(pm, 2, 3, READ_BDHI))
3205 return;
3206
3207 /* For the moment don't do the palette test - it's a waste of time when
3208 * compared to the greyscale test.
3209 */
3210#if 0
3211 if (!test_size(pm, 3, 0, 3))
3212 return;
3213#endif
3214
3215 if (!test_size(pm, 4, 3, READ_BDHI))
3216 return;
3217
3218 if (!test_size(pm, 6, 3, READ_BDHI))
3219 return;
3220}
3221
3222
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003223/********************************* GAMMA TESTS ********************************/
3224/* Gamma test images. */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003225typedef struct gamma_modification
3226{
3227 png_modification this;
3228 png_fixed_point gamma;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003229} gamma_modification;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003230
3231static int
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003232gamma_modify(png_modifier *pm, png_modification *me, int add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003233{
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05003234 UNUSED(add);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003235 /* This simply dumps the given gamma value into the buffer. */
3236 png_save_uint_32(pm->buffer, 4);
3237 png_save_uint_32(pm->buffer+4, CHUNK_gAMA);
3238 png_save_uint_32(pm->buffer+8, ((gamma_modification*)me)->gamma);
3239 return 1;
3240}
3241
3242static void
John Bowler168a4332011-01-16 19:32:22 -06003243gamma_modification_init(gamma_modification *me, png_modifier *pm, double gammad)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003244{
Glenn Randers-Pehrson21b4b332010-08-18 07:12:38 -05003245 double g;
3246
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003247 modification_init(&me->this);
3248 me->this.chunk = CHUNK_gAMA;
3249 me->this.modify_fn = gamma_modify;
3250 me->this.add = CHUNK_PLTE;
John Bowler168a4332011-01-16 19:32:22 -06003251 g = floor(gammad * 100000 + .5);
Glenn Randers-Pehrson21b4b332010-08-18 07:12:38 -05003252 me->gamma = (png_fixed_point)g;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003253 me->this.next = pm->modifications;
3254 pm->modifications = &me->this;
3255}
3256
3257typedef struct srgb_modification
3258{
3259 png_modification this;
3260 png_byte intent;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003261} srgb_modification;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003262
3263static int
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003264srgb_modify(png_modifier *pm, png_modification *me, int add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003265{
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05003266 UNUSED(add);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003267 /* As above, ignore add and just make a new chunk */
3268 png_save_uint_32(pm->buffer, 1);
3269 png_save_uint_32(pm->buffer+4, CHUNK_sRGB);
3270 pm->buffer[8] = ((srgb_modification*)me)->intent;
3271 return 1;
3272}
3273
3274static void
3275srgb_modification_init(srgb_modification *me, png_modifier *pm, png_byte intent)
3276{
3277 modification_init(&me->this);
3278 me->this.chunk = CHUNK_sBIT;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003279
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003280 if (intent <= 3) /* if valid, else *delete* sRGB chunks */
3281 {
3282 me->this.modify_fn = srgb_modify;
3283 me->this.add = CHUNK_PLTE;
3284 me->intent = intent;
3285 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003286
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003287 else
3288 {
3289 me->this.modify_fn = 0;
3290 me->this.add = 0;
3291 me->intent = 0;
3292 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003293
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003294 me->this.next = pm->modifications;
3295 pm->modifications = &me->this;
3296}
3297
3298typedef struct sbit_modification
3299{
3300 png_modification this;
3301 png_byte sbit;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003302} sbit_modification;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003303
3304static int
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003305sbit_modify(png_modifier *pm, png_modification *me, int add)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003306{
3307 png_byte sbit = ((sbit_modification*)me)->sbit;
3308 if (pm->bit_depth > sbit)
3309 {
3310 int cb = 0;
3311 switch (pm->colour_type)
3312 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003313 case 0:
3314 cb = 1;
3315 break;
3316
3317 case 2:
3318 case 3:
3319 cb = 3;
3320 break;
3321
3322 case 4:
3323 cb = 2;
3324 break;
3325
3326 case 6:
3327 cb = 4;
3328 break;
3329
3330 default:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003331 png_error(pm->this.pread,
3332 "unexpected colour type in sBIT modification");
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003333 }
3334
3335 png_save_uint_32(pm->buffer, cb);
3336 png_save_uint_32(pm->buffer+4, CHUNK_sBIT);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003337
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003338 while (cb > 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05003339 (pm->buffer+8)[--cb] = sbit;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003340
3341 return 1;
3342 }
3343 else if (!add)
3344 {
3345 /* Remove the sBIT chunk */
3346 pm->buffer_count = pm->buffer_position = 0;
3347 return 1;
3348 }
3349 else
3350 return 0; /* do nothing */
3351}
3352
3353static void
3354sbit_modification_init(sbit_modification *me, png_modifier *pm, png_byte sbit)
3355{
3356 modification_init(&me->this);
3357 me->this.chunk = CHUNK_sBIT;
3358 me->this.modify_fn = sbit_modify;
3359 me->this.add = CHUNK_PLTE;
3360 me->sbit = sbit;
3361 me->this.next = pm->modifications;
3362 pm->modifications = &me->this;
3363}
3364
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003365/* Reader callbacks and implementations, where they differ from the standard
3366 * ones.
3367 */
3368typedef struct gamma_display
3369{
3370 standard_display this;
3371
3372 /* Parameters */
3373 png_modifier* pm;
3374 double file_gamma;
3375 double screen_gamma;
3376 png_byte sbit;
3377 int threshold_test;
3378 PNG_CONST char* name;
3379 int speed;
3380 int use_input_precision;
3381 int strip16;
3382
3383 /* Local variables */
3384 double maxerrout;
3385 double maxerrpc;
3386 double maxerrabs;
3387} gamma_display;
3388
3389static void
John Bowler660c6e42010-12-19 06:22:23 -06003390gamma_display_init(gamma_display *dp, png_modifier *pm, png_uint_32 id,
3391 double file_gamma, double screen_gamma, png_byte sbit, int threshold_test,
3392 int speed, int use_input_precision, int strip16)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003393{
3394 /* Standard fields */
John Bowler660c6e42010-12-19 06:22:23 -06003395 standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/);
Glenn Randers-Pehrsona5815562010-11-20 21:48:29 -06003396
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003397 /* Parameter fields */
3398 dp->pm = pm;
3399 dp->file_gamma = file_gamma;
3400 dp->screen_gamma = screen_gamma;
3401 dp->sbit = sbit;
3402 dp->threshold_test = threshold_test;
3403 dp->speed = speed;
3404 dp->use_input_precision = use_input_precision;
3405 dp->strip16 = strip16;
3406
3407 /* Local variable fields */
3408 dp->maxerrout = dp->maxerrpc = dp->maxerrabs = 0;
3409}
3410
3411static void
3412gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi)
3413{
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003414 /* Reuse the standard stuff as appropriate. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003415 standard_info_part1(&dp->this, pp, pi);
3416
3417 /* If requested strip 16 to 8 bits - this is handled automagically below
3418 * because the output bit depth is read from the library. Note that there
3419 * are interactions with sBIT but, internally, libpng makes sbit at most
3420 * PNG_MAX_GAMMA_8 when doing the following.
3421 */
3422 if (dp->strip16)
John Bowlerb54498e2010-12-08 16:26:21 -06003423# ifdef PNG_READ_16_TO_8_SUPPORTED
Glenn Randers-Pehrsonfded04f2010-08-27 14:21:21 -05003424 png_set_strip_16(pp);
3425# else
3426 png_error(pp, "strip16 (16 to 8 bit conversion) not supported");
3427# endif
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003428
3429 png_read_update_info(pp, pi);
3430
3431 /* Now we may get a different cbRow: */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003432 standard_info_part2(&dp->this, pp, pi, 1 /*images*/);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003433}
3434
3435static void
3436gamma_info(png_structp pp, png_infop pi)
3437{
3438 gamma_info_imp(png_get_progressive_ptr(pp), pp, pi);
3439}
3440
3441static void
3442gamma_image_validate(gamma_display *dp, png_structp pp, png_infop pi,
3443 png_const_bytep pRow)
3444{
3445 /* Get some constants derived from the input and output file formats: */
3446 PNG_CONST png_byte sbit = dp->sbit;
3447 PNG_CONST double file_gamma = dp->file_gamma;
3448 PNG_CONST double screen_gamma = dp->screen_gamma;
3449 PNG_CONST int use_input_precision = dp->use_input_precision;
3450 PNG_CONST int speed = dp->speed;
3451 PNG_CONST png_byte in_ct = dp->this.colour_type;
3452 PNG_CONST png_byte in_bd = dp->this.bit_depth;
3453 PNG_CONST png_uint_32 w = dp->this.w;
3454 PNG_CONST png_uint_32 h = dp->this.h;
3455 PNG_CONST size_t cbRow = dp->this.cbRow;
3456 PNG_CONST png_byte out_ct = png_get_color_type(pp, pi);
3457 PNG_CONST png_byte out_bd = png_get_bit_depth(pp, pi);
3458 PNG_CONST unsigned int outmax = (1U<<out_bd)-1;
3459 PNG_CONST double maxabs = abserr(dp->pm, out_bd);
3460 PNG_CONST double maxout = outerr(dp->pm, out_bd);
3461 PNG_CONST double maxpc = pcerr(dp->pm, out_bd);
3462
3463 /* There are three sources of error, firstly the quantization in the
3464 * file encoding, determined by sbit and/or the file depth, secondly
3465 * the output (screen) gamma and thirdly the output file encoding.
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003466 *
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003467 * Since this API receives the screen and file gamma in double
3468 * precision it is possible to calculate an exact answer given an input
3469 * pixel value. Therefore we assume that the *input* value is exact -
3470 * sample/maxsample - calculate the corresponding gamma corrected
3471 * output to the limits of double precision arithmetic and compare with
3472 * what libpng returns.
3473 *
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003474 * Since the library must quantize the output to 8 or 16 bits there is
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003475 * a fundamental limit on the accuracy of the output of +/-.5 - this
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003476 * quantization limit is included in addition to the other limits
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003477 * specified by the paramaters to the API. (Effectively, add .5
3478 * everywhere.)
3479 *
3480 * The behavior of the 'sbit' paramter is defined by section 12.5
3481 * (sample depth scaling) of the PNG spec. That section forces the
3482 * decoder to assume that the PNG values have been scaled if sBIT is
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003483 * present:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003484 *
Glenn Randers-Pehrsonbc363ec2010-10-12 21:17:00 -05003485 * png-sample = floor( input-sample * (max-out/max-in) + .5);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003486 *
3487 * This means that only a subset of the possible PNG values should
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003488 * appear in the input. However, the spec allows the encoder to use a
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003489 * variety of approximations to the above and doesn't require any
3490 * restriction of the values produced.
3491 *
3492 * Nevertheless the spec requires that the upper 'sBIT' bits of the
3493 * value stored in a PNG file be the original sample bits.
3494 * Consequently the code below simply scales the top sbit bits by
3495 * (1<<sbit)-1 to obtain an original sample value.
3496 *
3497 * Because there is limited precision in the input it is arguable that
3498 * an acceptable result is any valid result from input-.5 to input+.5.
3499 * The basic tests below do not do this, however if
3500 * 'use_input_precision' is set a subsequent test is performed below.
3501 */
3502 PNG_CONST int processing = (fabs(screen_gamma*file_gamma-1) >=
3503 PNG_GAMMA_THRESHOLD && !dp->threshold_test && !speed && in_ct != 3) ||
3504 in_bd != out_bd;
3505
3506 PNG_CONST unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U;
3507
John Bowler168a4332011-01-16 19:32:22 -06003508 PNG_CONST double gamma_correction = 1/(file_gamma*screen_gamma);/* Overall */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003509
3510 double maxerrout = 0, maxerrabs = 0, maxerrpc = 0;
3511 png_uint_32 y;
3512
3513 for (y=0; y<h; ++y, pRow += cbRow)
3514 {
3515 unsigned int s, x;
John Bowler660c6e42010-12-19 06:22:23 -06003516 png_byte std[STANDARD_ROWMAX];
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003517
John Bowler660c6e42010-12-19 06:22:23 -06003518 transform_row(pp, std, in_ct, in_bd, y);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003519
3520 if (processing)
3521 {
3522 for (x=0; x<w; ++x) for (s=0; s<samples_per_pixel; ++s)
3523 {
3524 /* Input sample values: */
3525 PNG_CONST unsigned int
3526 id = sample(std, in_ct, in_bd, x, s);
3527
3528 PNG_CONST unsigned int
3529 od = sample(pRow, out_ct, out_bd, x, s);
3530
3531 PNG_CONST unsigned int
3532 isbit = id >> (in_bd-sbit);
3533
John Bowler168a4332011-01-16 19:32:22 -06003534 double i, input_sample, encoded_sample, output;
3535 double encoded_error, error;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003536 double es_lo, es_hi;
3537
3538 /* First check on the 'perfect' result obtained from the
3539 * digitized input value, id, and compare this against the
3540 * actual digitized result, 'od'. 'i' is the input result
3541 * in the range 0..1:
3542 *
3543 * NOTE: sBIT should be taken into account here but isn't,
3544 * as described above.
3545 */
3546 i = isbit; i /= (1U<<sbit)-1;
3547
3548 /* Then get the gamma corrected version of 'i' and compare
3549 * to 'od', any error less than .5 is insignificant - just
3550 * quantization of the output value to the nearest digital
3551 * value (nevertheless the error is still recorded - it's
3552 * interesting ;-)
3553 */
John Bowler168a4332011-01-16 19:32:22 -06003554 encoded_sample = pow(i, gamma_correction) * outmax;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003555 encoded_error = fabs(od-encoded_sample);
3556
3557 if (encoded_error > maxerrout)
3558 maxerrout = encoded_error;
3559
3560 if (encoded_error < .5+maxout)
3561 continue;
3562
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003563 /* There may be an error, so calculate the actual sample
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003564 * values - unencoded light intensity values. Note that
3565 * in practice these are not unencoded because they
3566 * include a 'viewing correction' to decrease or
3567 * (normally) increase the perceptual contrast of the
3568 * image. There's nothing we can do about this - we don't
3569 * know what it is - so assume the unencoded value is
3570 * perceptually linear.
3571 */
John Bowler168a4332011-01-16 19:32:22 -06003572 input_sample = pow(i, 1/file_gamma); /* In range 0..1 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003573 output = od;
3574 output /= outmax;
3575 output = pow(output, screen_gamma);
3576
3577 /* Now we have the numbers for real errors, both absolute
3578 * values as as a percentage of the correct value (output):
3579 */
John Bowler168a4332011-01-16 19:32:22 -06003580 error = fabs(input_sample-output);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003581
3582 if (error > maxerrabs)
3583 maxerrabs = error;
3584
3585 /* The following is an attempt to ignore the tendency of
3586 * quantization to dominate the percentage errors for low
3587 * output sample values:
3588 */
John Bowler168a4332011-01-16 19:32:22 -06003589 if (input_sample*maxpc > .5+maxabs)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003590 {
John Bowler168a4332011-01-16 19:32:22 -06003591 double percentage_error = error/input_sample;
3592 if (percentage_error > maxerrpc) maxerrpc = percentage_error;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003593 }
3594
3595 /* Now calculate the digitization limits for
3596 * 'encoded_sample' using the 'max' values. Note that
3597 * maxout is in the encoded space but maxpc and maxabs are
3598 * in linear light space.
3599 *
3600 * First find the maximum error in linear light space,
3601 * range 0..1:
3602 */
3603 {
John Bowler168a4332011-01-16 19:32:22 -06003604 double tmp = input_sample * maxpc;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003605 if (tmp < maxabs) tmp = maxabs;
3606
3607 /* Low bound - the minimum of the three: */
3608 es_lo = encoded_sample - maxout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003609
John Bowler168a4332011-01-16 19:32:22 -06003610 if (es_lo > 0 && input_sample-tmp > 0)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003611 {
John Bowler168a4332011-01-16 19:32:22 -06003612 double low_value = outmax * pow(input_sample-tmp,
3613 1/screen_gamma);
3614 if (low_value < es_lo) es_lo = low_value;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003615 }
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003616
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003617 else
3618 es_lo = 0;
3619
3620 es_hi = encoded_sample + maxout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003621
John Bowler168a4332011-01-16 19:32:22 -06003622 if (es_hi < outmax && input_sample+tmp < 1)
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003623 {
John Bowler168a4332011-01-16 19:32:22 -06003624 double high_value = outmax * pow(input_sample+tmp,
3625 1/screen_gamma);
3626 if (high_value > es_hi) es_hi = high_value;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003627 }
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003628
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003629 else
3630 es_hi = outmax;
3631 }
3632
3633 /* The primary test is that the final encoded value
3634 * returned by the library should be between the two limits
3635 * (inclusive) that were calculated above. At this point
3636 * quantization of the output must be taken into account.
3637 */
3638 if (od+.5 < es_lo || od-.5 > es_hi)
3639 {
3640 /* There has been an error in processing. */
3641 double is_lo, is_hi;
3642
3643 if (use_input_precision)
3644 {
3645 /* Ok, something is wrong - this actually happens in
3646 * current libpng sbit processing. Assume that the
3647 * input value (id, adjusted for sbit) can be
3648 * anywhere between value-.5 and value+.5 - quite a
3649 * large range if sbit is low.
3650 */
3651 double tmp = (isbit - .5)/((1U<<sbit)-1);
3652
3653 if (tmp > 0)
3654 {
John Bowler168a4332011-01-16 19:32:22 -06003655 is_lo = outmax * pow(tmp, gamma_correction) - maxout;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003656 if (is_lo < 0) is_lo = 0;
3657 }
3658
3659 else
3660 is_lo = 0;
3661
3662 tmp = (isbit + .5)/((1U<<sbit)-1);
3663
3664 if (tmp < 1)
3665 {
John Bowler168a4332011-01-16 19:32:22 -06003666 is_hi = outmax * pow(tmp, gamma_correction) + maxout;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003667 if (is_hi > outmax) is_hi = outmax;
3668 }
3669
3670 else
3671 is_hi = outmax;
3672
3673 if (!(od+.5 < is_lo || od-.5 > is_hi))
3674 continue;
3675 }
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003676 else
3677 is_lo = es_lo, is_hi = es_hi;
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003678
3679 {
3680 char msg[256];
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003681
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003682 sprintf(msg,
3683 "error: %.3f; %u{%u;%u} -> %u not %.2f (%.1f-%.1f)",
3684 od-encoded_sample, id, sbit, isbit, od,
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003685 encoded_sample, is_lo, is_hi);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003686
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003687 png_warning(pp, msg);
3688 }
3689 }
3690 }
3691 }
3692
3693 else if (!speed && memcmp(std, pRow, cbRow) != 0)
3694 {
3695 char msg[64];
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003696
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003697 /* No transform is expected on the threshold tests. */
3698 sprintf(msg, "gamma: below threshold row %d changed", y);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003699
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003700 png_error(pp, msg);
3701 }
3702 } /* row (y) loop */
3703
3704 dp->maxerrout = maxerrout;
3705 dp->maxerrabs = maxerrabs;
3706 dp->maxerrpc = maxerrpc;
3707 dp->this.ps->validated = 1;
3708}
3709
3710static void
3711gamma_end(png_structp pp, png_infop pi)
3712{
3713 gamma_display *dp = png_get_progressive_ptr(pp);
3714
3715 gamma_image_validate(dp, pp, pi, dp->this.ps->image);
3716}
3717
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003718/* A single test run checking a gamma transformation.
3719 *
3720 * maxabs: maximum absolute error as a fraction
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003721 * maxout: maximum output error in the output units
3722 * maxpc: maximum percentage error (as a percentage)
3723 */
3724static void
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003725gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn,
3726 PNG_CONST png_byte bit_depthIn, PNG_CONST int interlace_typeIn,
3727 PNG_CONST double file_gammaIn, PNG_CONST double screen_gammaIn,
3728 PNG_CONST png_byte sbitIn, PNG_CONST int threshold_testIn,
3729 PNG_CONST char *name, PNG_CONST int speedIn,
3730 PNG_CONST int use_input_precisionIn, PNG_CONST int strip16In)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003731{
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003732 gamma_display d;
3733 context(&pmIn->this, fault);
3734
John Bowler660c6e42010-12-19 06:22:23 -06003735 gamma_display_init(&d, pmIn, FILEID(colour_typeIn, bit_depthIn,
3736 interlace_typeIn, 0, 0, 0), file_gammaIn, screen_gammaIn, sbitIn,
3737 threshold_testIn, speedIn, use_input_precisionIn, strip16In);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003738
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003739 Try
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003740 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003741 png_structp pp;
3742 png_infop pi;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003743 gamma_modification gamma_mod;
3744 srgb_modification srgb_mod;
3745 sbit_modification sbit_mod;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003746
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003747 /* Make an appropriate modifier to set the PNG file gamma to the
3748 * given gamma value and the sBIT chunk to the given precision.
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003749 */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003750 d.pm->modifications = NULL;
3751 gamma_modification_init(&gamma_mod, d.pm, d.file_gamma);
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06003752 srgb_modification_init(&srgb_mod, d.pm, 127 /*delete*/);
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003753 sbit_modification_init(&sbit_mod, d.pm, d.sbit);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003754
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003755 modification_reset(d.pm->modifications);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003756
Glenn Randers-Pehrsonf18a0ed2010-08-24 08:41:00 -05003757 /* Get a png_struct for writing the image. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003758 pp = set_modifier_for_read(d.pm, &pi, d.this.id, name);
Glenn Randers-Pehrson949d46c2010-08-24 08:29:58 -05003759
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003760 /* Set up gamma processing. */
Glenn Randers-Pehrson15333cd2010-08-24 15:29:52 -05003761#ifdef PNG_FLOATING_POINT_SUPPORTED
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003762 png_set_gamma(pp, d.screen_gamma, d.file_gamma);
Glenn Randers-Pehrson15333cd2010-08-24 15:29:52 -05003763#else
3764 {
3765 png_fixed_point s = floor(d.screen_gamma*100000+.5);
3766 png_fixed_point f = floor(d.file_gamma*100000+.5);
3767 png_set_gamma_fixed(pp, s, f);
3768 }
3769#endif
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003770
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003771 /* Introduce the correct read function. */
3772 if (d.pm->this.progressive)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003773 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003774 /* Share the row function with the standard implementation. */
3775 png_set_progressive_read_fn(pp, &d, gamma_info, progressive_row,
3776 gamma_end);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003777
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003778 /* Now feed data into the reader until we reach the end: */
3779 modifier_progressive_read(d.pm, pp, pi);
3780 }
3781 else
3782 {
3783 /* modifier_read expects a png_modifier* */
3784 png_set_read_fn(pp, d.pm, modifier_read);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003785
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003786 /* Check the header values: */
3787 png_read_info(pp, pi);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003788
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003789 /* Process the 'info' requirements. Only one image is generated */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003790 gamma_info_imp(&d, pp, pi);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003791
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003792 sequential_row(&d.this, pp, pi, NULL, d.this.ps->image);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003793
Glenn Randers-Pehrson9b780b82010-08-24 08:50:01 -05003794 gamma_image_validate(&d, pp, pi, d.this.ps->image);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003795 }
3796
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003797 modifier_reset(d.pm);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003798
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003799 if (d.pm->log && !d.threshold_test && !d.speed)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003800 fprintf(stderr, "%d bit %s %s: max error %f (%.2g, %2g%%)\n",
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003801 d.this.bit_depth, colour_types[d.this.colour_type], d.name,
3802 d.maxerrout, d.maxerrabs, 100*d.maxerrpc);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003803
3804 /* Log the summary values too. */
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003805 if (d.this.colour_type == 0 || d.this.colour_type == 4)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003806 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003807 switch (d.this.bit_depth)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05003808 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003809 case 1:
3810 break;
3811
3812 case 2:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003813 if (d.maxerrout > d.pm->error_gray_2)
3814 d.pm->error_gray_2 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003815
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003816 break;
3817
3818 case 4:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003819 if (d.maxerrout > d.pm->error_gray_4)
3820 d.pm->error_gray_4 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003821
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003822 break;
3823
3824 case 8:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003825 if (d.maxerrout > d.pm->error_gray_8)
3826 d.pm->error_gray_8 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003827
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003828 break;
3829
3830 case 16:
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003831 if (d.maxerrout > d.pm->error_gray_16)
3832 d.pm->error_gray_16 = d.maxerrout;
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003833
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003834 break;
3835
3836 default:
3837 png_error(pp, "bad bit depth (internal: 1)");
3838 }
3839 }
3840
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003841 else if (d.this.colour_type == 2 || d.this.colour_type == 6)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003842 {
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003843 switch (d.this.bit_depth)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003844 {
3845 case 8:
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003846
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003847 if (d.maxerrout > d.pm->error_color_8)
3848 d.pm->error_color_8 = d.maxerrout;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003849
3850 break;
3851
3852 case 16:
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003853
Glenn Randers-Pehrson0f211612010-08-24 08:46:53 -05003854 if (d.maxerrout > d.pm->error_color_16)
3855 d.pm->error_color_16 = d.maxerrout;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003856
3857 break;
3858
3859 default:
3860 png_error(pp, "bad bit depth (internal: 2)");
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05003861 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003862 }
3863 }
3864
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003865 Catch(fault)
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05003866 modifier_reset((png_modifier*)fault);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003867}
3868
3869static void gamma_threshold_test(png_modifier *pm, png_byte colour_type,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003870 png_byte bit_depth, int interlace_type, double file_gamma,
3871 double screen_gamma)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003872{
3873 size_t pos = 0;
3874 char name[64];
3875 pos = safecat(name, sizeof name, pos, "threshold ");
3876 pos = safecatd(name, sizeof name, pos, file_gamma, 3);
3877 pos = safecat(name, sizeof name, pos, "/");
3878 pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
3879
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003880 (void)gamma_test(pm, colour_type, bit_depth, interlace_type, file_gamma,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06003881 screen_gamma, bit_depth, 1, name, 0 /*speed*/, 0 /*no input precision*/,
3882 0 /*no strip16*/);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003883}
3884
3885static void
3886perform_gamma_threshold_tests(png_modifier *pm)
3887{
3888 png_byte colour_type = 0;
3889 png_byte bit_depth = 0;
3890
3891 while (next_format(&colour_type, &bit_depth))
3892 {
John Bowler168a4332011-01-16 19:32:22 -06003893 double test_gamma = 1.0;
3894 while (test_gamma >= .4)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003895 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003896 /* There's little point testing the interlacing vs non-interlacing,
3897 * but this can be set from the command line.
3898 */
3899 gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
John Bowler168a4332011-01-16 19:32:22 -06003900 test_gamma, 1/test_gamma);
3901 test_gamma *= .95;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003902 }
3903
3904 /* And a special test for sRGB */
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003905 gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
3906 .45455, 2.2);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003907
3908 if (fail(pm))
3909 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003910 }
3911}
3912
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05003913static void gamma_transform_test(png_modifier *pm,
3914 PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth,
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003915 PNG_CONST int interlace_type, PNG_CONST double file_gamma,
3916 PNG_CONST double screen_gamma, PNG_CONST png_byte sbit, PNG_CONST int speed,
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05003917 PNG_CONST int use_input_precision, PNG_CONST int strip16)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003918{
3919 size_t pos = 0;
3920 char name[64];
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003921
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003922 if (sbit != bit_depth)
3923 {
3924 pos = safecat(name, sizeof name, pos, "sbit(");
3925 pos = safecatn(name, sizeof name, pos, sbit);
3926 pos = safecat(name, sizeof name, pos, ") ");
3927 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003928
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003929 else
3930 pos = safecat(name, sizeof name, pos, "gamma ");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003931
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003932 if (strip16)
3933 pos = safecat(name, sizeof name, pos, "16to8 ");
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06003934
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003935 pos = safecatd(name, sizeof name, pos, file_gamma, 3);
3936 pos = safecat(name, sizeof name, pos, "->");
3937 pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
3938
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003939 gamma_test(pm, colour_type, bit_depth, interlace_type, file_gamma,
3940 screen_gamma, sbit, 0, name, speed, use_input_precision, strip16);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003941}
3942
3943static void perform_gamma_transform_tests(png_modifier *pm, int speed)
3944{
3945 png_byte colour_type = 0;
3946 png_byte bit_depth = 0;
3947
3948 /* Ignore palette images - the gamma correction happens on the palette entry,
3949 * haven't got the tests for this yet.
3950 */
3951 while (next_format(&colour_type, &bit_depth)) if (colour_type != 3)
3952 {
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05003953 unsigned int i, j;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003954
3955 for (i=0; i<pm->ngammas; ++i) for (j=0; j<pm->ngammas; ++j) if (i != j)
3956 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003957 gamma_transform_test(pm, colour_type, bit_depth, pm->interlace_type,
3958 1/pm->gammas[i], pm->gammas[j], bit_depth, speed,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06003959 pm->use_input_precision, 0 /*do not strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003960
3961 if (fail(pm))
3962 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003963 }
3964 }
3965}
3966
3967static void perform_gamma_sbit_tests(png_modifier *pm, int speed)
3968{
3969 png_byte sbit;
3970
3971 /* The only interesting cases are colour and grayscale, alpha is ignored here
3972 * for overall speed. Only bit depths 8 and 16 are tested.
3973 */
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05003974 for (sbit=pm->sbitlow; sbit<(1<<READ_BDHI); ++sbit)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003975 {
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05003976 unsigned int i, j;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003977
3978 for (i=0; i<pm->ngammas; ++i)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05003979 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003980 for (j=0; j<pm->ngammas; ++j)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05003981 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003982 if (i != j)
3983 {
3984 if (sbit < 8)
3985 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003986 gamma_transform_test(pm, 0, 8, pm->interlace_type,
3987 1/pm->gammas[i], pm->gammas[j], sbit, speed,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06003988 pm->use_input_precision_sbit, 0 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003989
3990 if (fail(pm))
3991 return;
3992
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05003993 gamma_transform_test(pm, 2, 8, pm->interlace_type,
3994 1/pm->gammas[i], pm->gammas[j], sbit, speed,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06003995 pm->use_input_precision_sbit, 0 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05003996
3997 if (fail(pm))
3998 return;
3999 }
4000
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05004001#ifdef DO_16BIT
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004002 gamma_transform_test(pm, 0, 16, pm->interlace_type,
4003 1/pm->gammas[i], pm->gammas[j], sbit, speed,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06004004 pm->use_input_precision_sbit, 0 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004005
4006 if (fail(pm))
4007 return;
4008
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004009 gamma_transform_test(pm, 2, 16, pm->interlace_type,
4010 1/pm->gammas[i], pm->gammas[j], sbit, speed,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06004011 pm->use_input_precision_sbit, 0 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004012
4013 if (fail(pm))
4014 return;
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05004015#endif
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004016 }
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004017 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004018 }
4019 }
4020}
4021
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05004022/* Note that this requires a 16 bit source image but produces 8 bit output, so
4023 * we only need the 16bit write support.
4024 */
John Bowlerb54498e2010-12-08 16:26:21 -06004025#ifdef PNG_READ_16_TO_8_SUPPORTED
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004026static void perform_gamma_strip16_tests(png_modifier *pm, int speed)
4027{
4028# ifndef PNG_MAX_GAMMA_8
4029# define PNG_MAX_GAMMA_8 11
4030# endif
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06004031 /* Include the alpha cases here. Note that sbit matches the internal value
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004032 * used by the library - otherwise we will get spurious errors from the
4033 * internal sbit style approximation.
4034 *
Glenn Randers-Pehrson233357e2010-07-29 21:49:38 -05004035 * The threshold test is here because otherwise the 16 to 8 conversion will
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004036 * proceed *without* gamma correction, and the tests above will fail (but not
4037 * by much) - this could be fixed, it only appears with the -g option.
4038 */
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05004039 unsigned int i, j;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004040 for (i=0; i<pm->ngammas; ++i)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004041 {
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004042 for (j=0; j<pm->ngammas; ++j)
4043 {
4044 if (i != j &&
4045 fabs(pm->gammas[j]/pm->gammas[i]-1) >= PNG_GAMMA_THRESHOLD)
4046 {
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004047 gamma_transform_test(pm, 0, 16, pm->interlace_type, 1/pm->gammas[i],
4048 pm->gammas[j], PNG_MAX_GAMMA_8, speed,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06004049 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004050
4051 if (fail(pm))
4052 return;
4053
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004054 gamma_transform_test(pm, 2, 16, pm->interlace_type, 1/pm->gammas[i],
4055 pm->gammas[j], PNG_MAX_GAMMA_8, speed,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06004056 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004057
4058 if (fail(pm))
4059 return;
4060
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004061 gamma_transform_test(pm, 4, 16, pm->interlace_type, 1/pm->gammas[i],
4062 pm->gammas[j], PNG_MAX_GAMMA_8, speed,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06004063 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004064
4065 if (fail(pm))
4066 return;
4067
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004068 gamma_transform_test(pm, 6, 16, pm->interlace_type, 1/pm->gammas[i],
4069 pm->gammas[j], PNG_MAX_GAMMA_8, speed,
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06004070 pm->use_input_precision_16to8, 1 /*strip16*/);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004071
4072 if (fail(pm))
4073 return;
4074 }
4075 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004076 }
4077}
Glenn Randers-Pehrsonfded04f2010-08-27 14:21:21 -05004078#endif /* 16 to 8 bit conversion */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004079
4080static void
4081perform_gamma_test(png_modifier *pm, int speed, int summary)
4082{
4083 /* First some arbitrary no-transform tests: */
John Bowlerb54498e2010-12-08 16:26:21 -06004084 if (!speed && pm->test_threshold)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004085 {
4086 perform_gamma_threshold_tests(pm);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004087
4088 if (fail(pm))
4089 return;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004090 }
4091
4092 /* Now some real transforms. */
John Bowlerb54498e2010-12-08 16:26:21 -06004093 if (pm->test_transform)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004094 {
John Bowlerb54498e2010-12-08 16:26:21 -06004095 perform_gamma_transform_tests(pm, speed);
Glenn Randers-Pehrsonf1cf9022010-12-07 14:40:33 -06004096
John Bowlerb54498e2010-12-08 16:26:21 -06004097 if (summary)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004098 {
John Bowlerb54498e2010-12-08 16:26:21 -06004099 printf("Gamma correction error summary\n\n");
4100 printf("The printed value is the maximum error in the pixel values\n");
4101 printf("calculated by the libpng gamma correction code. The error\n");
4102 printf("is calculated as the difference between the output pixel\n");
4103 printf("value (always an integer) and the ideal value from the\n");
4104 printf("libpng specification (typically not an integer).\n\n");
4105
4106 printf("Expect this value to be less than .5 for 8 bit formats,\n");
4107 printf("less than 1 for formats with fewer than 8 bits and a small\n");
4108 printf("number (typically less than 5) for the 16 bit formats.\n");
4109 printf("For performance reasons the value for 16 bit formats\n");
4110 printf("increases when the image file includes an sBIT chunk.\n\n");
4111
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004112 printf(" 2 bit gray: %.5f\n", pm->error_gray_2);
4113 printf(" 4 bit gray: %.5f\n", pm->error_gray_4);
4114 printf(" 8 bit gray: %.5f\n", pm->error_gray_8);
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05004115 printf(" 8 bit color: %.5f\n", pm->error_color_8);
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05004116#ifdef DO_16BIT
John Bowlerb54498e2010-12-08 16:26:21 -06004117 printf(" 16 bit gray: %.5f\n", pm->error_gray_16);
4118 printf(" 16 bit color: %.5f\n", pm->error_color_16);
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05004119#endif
John Bowlerb54498e2010-12-08 16:26:21 -06004120 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004121 }
4122
John Bowlerb54498e2010-12-08 16:26:21 -06004123 /* The sbit tests produce much larger errors: */
4124 if (pm->test_sbit)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004125 {
John Bowlerb54498e2010-12-08 16:26:21 -06004126 pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 =
4127 pm->error_gray_16 = pm->error_color_8 = pm->error_color_16 = 0;
4128 perform_gamma_sbit_tests(pm, speed);
4129
4130 if (summary)
4131 {
4132 printf("Gamma correction with sBIT:\n");
4133
4134 if (pm->sbitlow < 8U)
4135 {
4136 printf(" 2 bit gray: %.5f\n", pm->error_gray_2);
4137 printf(" 4 bit gray: %.5f\n", pm->error_gray_4);
4138 printf(" 8 bit gray: %.5f\n", pm->error_gray_8);
4139 printf(" 8 bit color: %.5f\n", pm->error_color_8);
4140 }
4141
4142 #ifdef DO_16BIT
4143 printf(" 16 bit gray: %.5f\n", pm->error_gray_16);
4144 printf(" 16 bit color: %.5f\n", pm->error_color_16);
4145 #endif
4146 }
4147 }
4148
4149#ifdef PNG_READ_16_TO_8_SUPPORTED
4150 if (pm->test_strip16)
4151 {
4152 /* The 16 to 8 bit strip operations: */
4153 pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 =
4154 pm->error_gray_16 = pm->error_color_8 = pm->error_color_16 = 0;
4155 perform_gamma_strip16_tests(pm, speed);
4156
4157 if (summary)
4158 {
4159 printf("Gamma correction with 16 to 8 bit reduction:\n");
4160 printf(" 16 bit gray: %.5f\n", pm->error_gray_16);
4161 printf(" 16 bit color: %.5f\n", pm->error_color_16);
4162 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004163 }
Glenn Randers-Pehrson2f702822010-08-27 06:39:23 -05004164#endif
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004165}
4166
John Bowler660c6e42010-12-19 06:22:23 -06004167/* INTERLACE MACRO VALIDATION */
4168/* This is copied verbatim from the specification, it is simply the pass
4169 * number in which each pixel in each 8x8 tile appears. The array must
4170 * be indexed adam7[y][x] and notice that the pass numbers are based at
4171 * 1, not 0 - the base libpng uses.
4172 */
4173static PNG_CONST
4174png_byte adam7[8][8] =
4175{
4176 { 1,6,4,6,2,6,4,6 },
4177 { 7,7,7,7,7,7,7,7 },
4178 { 5,6,5,6,5,6,5,6 },
4179 { 7,7,7,7,7,7,7,7 },
4180 { 3,6,4,6,3,6,4,6 },
4181 { 7,7,7,7,7,7,7,7 },
4182 { 5,6,5,6,5,6,5,6 },
4183 { 7,7,7,7,7,7,7,7 }
4184};
4185
4186/* This routine validates all the interlace support macros in png.h for
4187 * a variety of valid PNG widths and heights. It uses a number of similarly
4188 * named internal routines that feed off the above array.
4189 */
4190static png_uint_32
4191png_pass_start_row(int pass)
4192{
4193 int x, y;
4194 ++pass;
4195 for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
4196 return y;
4197 return 0xf;
4198}
4199
4200static png_uint_32
4201png_pass_start_col(int pass)
4202{
4203 int x, y;
4204 ++pass;
4205 for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
4206 return x;
4207 return 0xf;
4208}
4209
4210static int
4211png_pass_row_shift(int pass)
4212{
4213 int x, y, base=(-1), inc=8;
4214 ++pass;
4215 for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
4216 {
4217 if (base == (-1))
4218 base = y;
4219 else if (base == y)
4220 {}
4221 else if (inc == y-base)
4222 base=y;
4223 else if (inc == 8)
4224 inc = y-base, base=y;
4225 else if (inc != y-base)
4226 return 0xff; /* error - more than one 'inc' value! */
4227 }
4228
4229 if (base == (-1)) return 0xfe; /* error - no row in pass! */
4230
4231 /* The shift is always 1, 2 or 3 - no pass has all the rows! */
4232 switch (inc)
4233 {
4234case 2: return 1;
4235case 4: return 2;
4236case 8: return 3;
4237default: break;
4238 }
4239
4240 /* error - unrecognized 'inc' */
4241 return (inc << 8) + 0xfd;
4242}
4243
4244static int
4245png_pass_col_shift(int pass)
4246{
4247 int x, y, base=(-1), inc=8;
4248 ++pass;
4249 for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
4250 {
4251 if (base == (-1))
4252 base = x;
4253 else if (base == x)
4254 {}
4255 else if (inc == x-base)
4256 base=x;
4257 else if (inc == 8)
4258 inc = x-base, base=x;
4259 else if (inc != x-base)
4260 return 0xff; /* error - more than one 'inc' value! */
4261 }
4262
4263 if (base == (-1)) return 0xfe; /* error - no row in pass! */
4264
4265 /* The shift is always 1, 2 or 3 - no pass has all the rows! */
4266 switch (inc)
4267 {
4268case 1: return 0; /* pass 7 has all the columns */
4269case 2: return 1;
4270case 4: return 2;
4271case 8: return 3;
4272default: break;
4273 }
4274
4275 /* error - unrecognized 'inc' */
4276 return (inc << 8) + 0xfd;
4277}
4278
4279static png_uint_32
4280png_row_from_pass_row(png_uint_32 yIn, int pass)
4281{
4282 /* By examination of the array: */
4283 switch (pass)
4284 {
4285case 0: return yIn * 8;
4286case 1: return yIn * 8;
4287case 2: return yIn * 8 + 4;
4288case 3: return yIn * 4;
4289case 4: return yIn * 4 + 2;
4290case 5: return yIn * 2;
4291case 6: return yIn * 2 + 1;
4292default: break;
4293 }
4294
4295 return 0xff; /* bad pass number */
4296}
4297
4298static png_uint_32
4299png_col_from_pass_col(png_uint_32 xIn, int pass)
4300{
4301 /* By examination of the array: */
4302 switch (pass)
4303 {
4304case 0: return xIn * 8;
4305case 1: return xIn * 8 + 4;
4306case 2: return xIn * 4;
4307case 3: return xIn * 4 + 2;
4308case 4: return xIn * 2;
4309case 5: return xIn * 2 + 1;
4310case 6: return xIn;
4311default: break;
4312 }
4313
4314 return 0xff; /* bad pass number */
4315}
4316
4317static int
4318png_row_in_interlace_pass(png_uint_32 y, int pass)
4319{
4320 /* Is row 'y' in pass 'pass'? */
4321 int x;
4322 y &= 7;
4323 ++pass;
4324 for (x=0; x<8; ++x) if (adam7[y][x] == pass)
4325 return 1;
4326
4327 return 0;
4328}
4329
4330static int
4331png_col_in_interlace_pass(png_uint_32 x, int pass)
4332{
4333 /* Is column 'x' in pass 'pass'? */
4334 int y;
4335 x &= 7;
4336 ++pass;
4337 for (y=0; y<8; ++y) if (adam7[y][x] == pass)
4338 return 1;
4339
4340 return 0;
4341}
4342
4343static png_uint_32
4344png_pass_rows(png_uint_32 height, int pass)
4345{
4346 png_uint_32 tiles = height>>3;
4347 png_uint_32 rows = 0;
4348 unsigned int x, y;
4349
4350 height &= 7;
4351 ++pass;
4352 for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
4353 {
4354 rows += tiles;
4355 if (y < height) ++rows;
4356 break; /* i.e. break the 'x', column, loop. */
4357 }
4358
4359 return rows;
4360}
4361
4362static png_uint_32
4363png_pass_cols(png_uint_32 width, int pass)
4364{
4365 png_uint_32 tiles = width>>3;
4366 png_uint_32 cols = 0;
4367 unsigned int x, y;
4368
4369 width &= 7;
4370 ++pass;
4371 for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
4372 {
4373 cols += tiles;
4374 if (x < width) ++cols;
4375 break; /* i.e. break the 'y', row, loop. */
4376 }
4377
4378 return cols;
4379}
4380
4381static void
4382perform_interlace_macro_validation(void)
4383{
4384 /* The macros to validate, first those that depend only on pass:
4385 *
4386 * PNG_PASS_START_ROW(pass)
4387 * PNG_PASS_START_COL(pass)
4388 * PNG_PASS_ROW_SHIFT(pass)
4389 * PNG_PASS_COL_SHIFT(pass)
4390 */
4391 int pass;
4392
4393 for (pass=0; pass<7; ++pass)
4394 {
4395 png_uint_32 m, f, v;
4396
4397 m = PNG_PASS_START_ROW(pass);
4398 f = png_pass_start_row(pass);
4399 if (m != f)
4400 {
4401 fprintf(stderr, "PNG_PASS_START_ROW(%d) = %u != %x\n", pass, m, f);
4402 exit(1);
4403 }
4404
4405 m = PNG_PASS_START_COL(pass);
4406 f = png_pass_start_col(pass);
4407 if (m != f)
4408 {
4409 fprintf(stderr, "PNG_PASS_START_COL(%d) = %u != %x\n", pass, m, f);
4410 exit(1);
4411 }
4412
4413 m = PNG_PASS_ROW_SHIFT(pass);
4414 f = png_pass_row_shift(pass);
4415 if (m != f)
4416 {
4417 fprintf(stderr, "PNG_PASS_ROW_SHIFT(%d) = %u != %x\n", pass, m, f);
4418 exit(1);
4419 }
4420
4421 m = PNG_PASS_COL_SHIFT(pass);
4422 f = png_pass_col_shift(pass);
4423 if (m != f)
4424 {
4425 fprintf(stderr, "PNG_PASS_COL_SHIFT(%d) = %u != %x\n", pass, m, f);
4426 exit(1);
4427 }
4428
4429 /* Macros that depend on the image or sub-image height too:
4430 *
4431 * PNG_PASS_ROWS(height, pass)
4432 * PNG_PASS_COLS(width, pass)
4433 * PNG_ROW_FROM_PASS_ROW(yIn, pass)
4434 * PNG_COL_FROM_PASS_COL(xIn, pass)
4435 * PNG_ROW_IN_INTERLACE_PASS(y, pass)
4436 * PNG_COL_IN_INTERLACE_PASS(x, pass)
4437 */
4438 for (v=0;;)
4439 {
4440 /* First the base 0 stuff: */
4441 m = PNG_ROW_FROM_PASS_ROW(v, pass);
4442 f = png_row_from_pass_row(v, pass);
4443 if (m != f)
4444 {
4445 fprintf(stderr, "PNG_ROW_FROM_PASS_ROW(%u, %d) = %u != %x\n",
4446 v, pass, m, f);
4447 exit(1);
4448 }
4449
4450 m = PNG_COL_FROM_PASS_COL(v, pass);
4451 f = png_col_from_pass_col(v, pass);
4452 if (m != f)
4453 {
4454 fprintf(stderr, "PNG_COL_FROM_PASS_COL(%u, %d) = %u != %x\n",
4455 v, pass, m, f);
4456 exit(1);
4457 }
4458
4459 m = PNG_ROW_IN_INTERLACE_PASS(v, pass);
4460 f = png_row_in_interlace_pass(v, pass);
4461 if (m != f)
4462 {
4463 fprintf(stderr, "PNG_ROW_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
4464 v, pass, m, f);
4465 exit(1);
4466 }
4467
4468 m = PNG_COL_IN_INTERLACE_PASS(v, pass);
4469 f = png_col_in_interlace_pass(v, pass);
4470 if (m != f)
4471 {
4472 fprintf(stderr, "PNG_COL_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
4473 v, pass, m, f);
4474 exit(1);
4475 }
4476
4477 /* Then the base 1 stuff: */
4478 ++v;
4479 m = PNG_PASS_ROWS(v, pass);
4480 f = png_pass_rows(v, pass);
4481 if (m != f)
4482 {
4483 fprintf(stderr, "PNG_PASS_ROWS(%u, %d) = %u != %x\n",
4484 v, pass, m, f);
4485 exit(1);
4486 }
4487
4488 m = PNG_PASS_COLS(v, pass);
4489 f = png_pass_cols(v, pass);
4490 if (m != f)
4491 {
4492 fprintf(stderr, "PNG_PASS_COLS(%u, %d) = %u != %x\n",
4493 v, pass, m, f);
4494 exit(1);
4495 }
4496
4497 /* Move to the next v - the stepping algorithm starts skipping
4498 * values above 1024.
4499 */
4500 if (v > 1024)
4501 {
4502 if (v == PNG_UINT_31_MAX)
4503 break;
4504
4505 v = (v << 1) ^ v;
4506 if (v >= PNG_UINT_31_MAX)
4507 v = PNG_UINT_31_MAX-1;
4508 }
4509 }
4510 }
4511}
4512
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004513/* main program */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004514int main(int argc, PNG_CONST char **argv)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004515{
John Bowler660c6e42010-12-19 06:22:23 -06004516 volatile int summary = 1; /* Print the error summary at the end */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004517
4518 /* Create the given output file on success: */
4519 PNG_CONST char *volatile touch = NULL;
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004520
4521 /* This is an array of standard gamma values (believe it or not I've seen
4522 * every one of these mentioned somewhere.)
4523 *
4524 * In the following list the most useful values are first!
4525 */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004526 static double
4527 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 -05004528
4529 png_modifier pm;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004530 context(&pm.this, fault);
4531
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004532 modifier_init(&pm);
4533
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004534 /* Preallocate the image buffer, because we know how big it needs to be,
4535 * note that, for testing purposes, it is deliberately mis-aligned.
4536 */
John Bowler660c6e42010-12-19 06:22:23 -06004537 pm.this.image = malloc(2*TRANSFORM_IMAGEMAX+1);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06004538
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004539 if (pm.this.image != NULL)
4540 {
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06004541 /* Ignore OOM at this point - the 'ensure' routine above will allocate
4542 * the array appropriately.
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004543 */
4544 ++(pm.this.image);
John Bowler660c6e42010-12-19 06:22:23 -06004545 pm.this.cb_image = 2*TRANSFORM_IMAGEMAX;
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004546 }
4547
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004548 /* Default to error on warning: */
4549 pm.this.treat_warnings_as_errors = 1;
4550
4551 /* Store the test gammas */
4552 pm.gammas = gammas;
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004553 pm.ngammas = 3U; /* for speed */
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004554 pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004555 pm.use_input_precision_16to8 = 1U; /* Because of the way libpng does it */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004556
Glenn Randers-Pehrsonf1cf9022010-12-07 14:40:33 -06004557 /* Some default values (set the behavior for 'make check' here).
4558 * These values simply control the maximum error permitted in the gamma
4559 * transformations. The practial limits for human perception are described
4560 * below (the setting for maxpc16), however for 8 bit encodings it isn't
4561 * possible to meet the accepted capabilities of human vision - i.e. 8 bit
Glenn Randers-Pehrson9f044c12010-12-07 14:59:43 -06004562 * images can never be good enough, regardless of encoding.
Glenn Randers-Pehrsonf1cf9022010-12-07 14:40:33 -06004563 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004564 pm.maxout8 = .1; /* Arithmetic error in *encoded* value */
4565 pm.maxabs8 = .00005; /* 1/20000 */
John Bowlerb54498e2010-12-08 16:26:21 -06004566 pm.maxpc8 = .499; /* I.e., .499% fractional error */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004567 pm.maxout16 = .499; /* Error in *encoded* value */
4568 pm.maxabs16 = .00005;/* 1/20000 */
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06004569
4570 /* NOTE: this is a reasonable perceptual limit. We assume that humans can
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004571 * perceive light level differences of 1% over a 100:1 range, so we need to
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06004572 * maintain 1 in 10000 accuracy (in linear light space), which is what the
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004573 * following guarantees. It also allows significantly higher errors at
4574 * higher 16 bit values, which is important for performance. The actual
4575 * maximum 16 bit error is about +/-1.9 in the fixed point implementation but
4576 * this is only allowed for values >38149 by the following:
4577 */
Glenn Randers-Pehrson67350582010-12-07 16:13:22 -06004578 pm.maxpc16 = .005; /* I.e., 1/200% - 1/20000 */
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004579
4580 /* Now parse the command line options. */
4581 while (--argc >= 1)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004582 {
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004583 if (strcmp(*++argv, "-v") == 0)
4584 pm.this.verbose = 1;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004585
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004586 else if (strcmp(*argv, "-l") == 0)
4587 pm.log = 1;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004588
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004589 else if (strcmp(*argv, "-q") == 0)
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05004590 summary = pm.this.verbose = pm.log = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004591
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004592 else if (strcmp(*argv, "-g") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004593 pm.ngammas = (sizeof gammas)/(sizeof gammas[0]);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004594
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004595 else if (strcmp(*argv, "-w") == 0)
4596 pm.this.treat_warnings_as_errors = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004597
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004598 else if (strcmp(*argv, "--speed") == 0)
John Bowlerb54498e2010-12-08 16:26:21 -06004599 pm.this.speed = 1, pm.ngammas = (sizeof gammas)/(sizeof gammas[0]),
4600 pm.test_standard = 0;
4601
John Bowler660c6e42010-12-19 06:22:23 -06004602 else if (strcmp(*argv, "--size") == 0)
4603 pm.test_size = 1;
4604
John Bowlerb54498e2010-12-08 16:26:21 -06004605 else if (strcmp(*argv, "--nostandard") == 0)
4606 pm.test_standard = 0;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004607
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05004608 else if (strcmp(*argv, "--nogamma") == 0)
4609 pm.ngammas = 0;
4610
John Bowlerb54498e2010-12-08 16:26:21 -06004611 else if (strcmp(*argv, "--nogamma-threshold") == 0)
4612 pm.test_threshold = 0;
4613
4614 else if (strcmp(*argv, "--nogamma-transform") == 0)
4615 pm.test_transform = 0;
4616
4617 else if (strcmp(*argv, "--nogamma-sbit") == 0)
4618 pm.test_sbit = 0;
4619
4620 else if (strcmp(*argv, "--nogamma-16-to-8") == 0)
4621 pm.test_strip16 = 0;
4622
Glenn Randers-Pehrsondb712a92010-08-24 08:44:14 -05004623 else if (strcmp(*argv, "--progressive-read") == 0)
4624 pm.this.progressive = 1;
4625
Glenn Randers-Pehrson21af4cc2010-08-24 08:33:28 -05004626 else if (strcmp(*argv, "--interlace") == 0)
4627 pm.interlace_type = PNG_INTERLACE_ADAM7;
4628
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004629 else if (argc >= 1 && strcmp(*argv, "--sbitlow") == 0)
4630 --argc, pm.sbitlow = (png_byte)atoi(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004631
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004632 else if (argc >= 1 && strcmp(*argv, "--touch") == 0)
4633 --argc, touch = *++argv;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004634
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004635 else if (argc >= 1 && strncmp(*argv, "--max", 4) == 0)
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004636 {
4637 --argc;
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004638
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004639 if (strcmp(4+*argv, "abs8") == 0)
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004640 pm.maxabs8 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004641
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004642 else if (strcmp(4+*argv, "abs16") == 0)
4643 pm.maxabs16 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004644
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004645 else if (strcmp(4+*argv, "out8") == 0)
4646 pm.maxout8 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004647
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004648 else if (strcmp(4+*argv, "out16") == 0)
4649 pm.maxout16 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004650
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004651 else if (strcmp(4+*argv, "pc8") == 0)
4652 pm.maxpc8 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004653
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004654 else if (strcmp(4+*argv, "pc16") == 0)
4655 pm.maxpc16 = atof(*++argv);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004656
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004657 else
4658 {
4659 fprintf(stderr, "pngvalid: %s: unknown 'max' option\n", *argv);
4660 exit(1);
4661 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004662 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004663
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004664 else
4665 {
4666 fprintf(stderr, "pngvalid: %s: unknown argument\n", *argv);
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004667 exit(1);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004668 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004669 }
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004670
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004671 Try
4672 {
4673 /* Make useful base images */
John Bowler660c6e42010-12-19 06:22:23 -06004674 make_transform_images(&pm.this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004675
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004676 /* Perform the standard and gamma tests. */
John Bowlerb54498e2010-12-08 16:26:21 -06004677 if (pm.test_standard)
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004678 {
John Bowler660c6e42010-12-19 06:22:23 -06004679 perform_interlace_macro_validation();
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004680 perform_standard_test(&pm);
4681 perform_error_test(&pm);
4682 }
4683
John Bowler660c6e42010-12-19 06:22:23 -06004684 /* Various oddly sized images: */
4685 if (pm.test_size)
4686 {
4687 make_size_images(&pm.this);
4688 perform_size_test(&pm);
4689 }
4690
John Bowlerb54498e2010-12-08 16:26:21 -06004691 if (pm.ngammas > 0)
4692 perform_gamma_test(&pm, pm.this.speed != 0,
4693 summary && !pm.this.speed);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004694 }
4695
4696 Catch(fault)
4697 {
Glenn Randers-Pehrson438b3ca2010-08-24 08:55:40 -05004698 fprintf(stderr, "pngvalid: test aborted (probably failed in cleanup)\n");
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05004699 if (!pm.this.verbose)
4700 {
4701 if (pm.this.error[0] != 0)
4702 fprintf(stderr, "pngvalid: first error: %s\n", pm.this.error);
Glenn Randers-Pehrson38ef3a52010-12-03 11:22:31 -06004703
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05004704 fprintf(stderr, "pngvalid: run with -v to see what happened\n");
4705 }
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004706 exit(1);
4707 }
4708
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05004709 if (summary && !pm.this.speed)
4710 {
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004711 printf("Results using %s point arithmetic %s\n",
4712#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || PNG_LIBPNG_VER < 10500
4713 "floating",
4714#else
4715 "fixed",
4716#endif
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004717 (pm.this.nerrors || (pm.this.treat_warnings_as_errors &&
4718 pm.this.nwarnings)) ? "(errors)" : (pm.this.nwarnings ?
Glenn Randers-Pehrson29034c52010-07-29 17:58:49 -05004719 "(warnings)" : "(no errors or warnings)")
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004720 );
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05004721 printf("Allocated memory statistics (in bytes):\n"
Glenn Randers-Pehrson9a75d992010-10-08 16:27:14 -05004722 "\tread %lu maximum single, %lu peak, %lu total\n"
4723 "\twrite %lu maximum single, %lu peak, %lu total\n",
4724 (unsigned long)pm.this.read_memory_pool.max_max,
4725 (unsigned long)pm.this.read_memory_pool.max_limit,
4726 (unsigned long)pm.this.read_memory_pool.max_total,
4727 (unsigned long)pm.this.write_memory_pool.max_max,
4728 (unsigned long)pm.this.write_memory_pool.max_limit,
4729 (unsigned long)pm.this.write_memory_pool.max_total);
Glenn Randers-Pehrson921d9152010-08-24 08:26:54 -05004730 }
4731
4732 /* Do this here to provoke memory corruption errors in memory not directly
4733 * allocated by libpng - not a complete test, but better than nothing.
4734 */
4735 store_delete(&pm.this);
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004736
4737 /* Error exit if there are any errors, and maybe if there are any
4738 * warnings.
4739 */
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004740 if (pm.this.nerrors || (pm.this.treat_warnings_as_errors &&
4741 pm.this.nwarnings))
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004742 {
4743 if (!pm.this.verbose)
4744 fprintf(stderr, "pngvalid: %s\n", pm.this.error);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004745
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004746 fprintf(stderr, "pngvalid: %d errors, %d warnings\n", pm.this.nerrors,
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004747 pm.this.nwarnings);
4748
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004749 exit(1);
4750 }
4751
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004752 /* Success case. */
4753 if (touch != NULL)
4754 {
4755 FILE *fsuccess = fopen(touch, "wt");
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004756
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004757 if (fsuccess != NULL)
4758 {
4759 int error = 0;
4760 fprintf(fsuccess, "PNG validation succeeded\n");
4761 fflush(fsuccess);
4762 error = ferror(fsuccess);
Glenn Randers-Pehrsonc08cae12010-08-20 09:55:01 -05004763
Glenn Randers-Pehrson77396b62010-08-02 08:00:10 -05004764 if (fclose(fsuccess) || error)
4765 {
4766 fprintf(stderr, "%s: write failed\n", touch);
4767 exit(1);
4768 }
4769 }
4770 }
4771
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -05004772 return 0;
4773}