]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | /* pngtest.c - a simple test program to test libpng | |
3 | * | |
4 | * libpng 1.0.3 -January 14, 1999 | |
5 | * For conditions of distribution and use, see copyright notice in png.h | |
6 | * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. | |
7 | * Copyright (c) 1996, 1997 Andreas Dilger | |
8 | * Copyright (c) 1998, 1999 Glenn Randers-Pehrson | |
9 | * | |
10 | * This program reads in a PNG image, writes it out again, and then | |
11 | * compares the two files. If the files are identical, this shows that | |
12 | * the basic chunk handling, filtering, and (de)compression code is working | |
13 | * properly. It does not currently test all of the transforms, although | |
14 | * it probably should. | |
15 | * | |
16 | * The program will report "FAIL" in certain legitimate cases: | |
17 | * 1) when the compression level or filter selection method is changed. | |
18 | * 2) when the chunk size is not 8K. | |
19 | * 3) unknown ancillary chunks exist in the input file. | |
20 | * 4) others not listed here... | |
21 | * In these cases, it is best to check with another tool such as "pngcheck" | |
22 | * to see what the differences between the two images are. | |
23 | * | |
24 | * If a filename is given on the command-line, then this file is used | |
25 | * for the input, rather than the default "pngtest.png". This allows | |
26 | * testing a wide variety of files easily. You can also test a number | |
27 | * of files at once by typing "pngtest -m file1.png file2.png ..." | |
28 | */ | |
29 | ||
30 | #include <stdio.h> | |
31 | #include <stdlib.h> | |
32 | ||
33 | /* Makes pngtest verbose so we can find problems (needs to be before png.h) */ | |
34 | #ifndef PNG_DEBUG | |
35 | #define PNG_DEBUG 0 | |
36 | #endif | |
37 | ||
38 | #include "png.h" | |
39 | ||
40 | #if defined(PNG_TIME_RFC1123_SUPPORTED) | |
41 | static int tIME_chunk_present=0; | |
42 | static char tIME_string[30] = "no tIME chunk present in file"; | |
43 | #endif /* PNG_TIME_RFC1123_SUPPORTED */ | |
44 | ||
45 | int test_one_file PNGARG((PNG_CONST char *inname, PNG_CONST char *outname)); | |
46 | ||
47 | #ifdef __TURBOC__ | |
48 | #include <mem.h> | |
49 | #endif | |
50 | ||
51 | /* defined so I can write to a file on gui/windowing platforms */ | |
52 | /* #define STDERR stderr */ | |
53 | #define STDERR stdout /* for DOS */ | |
54 | ||
55 | /* example of using row callbacks to make a simple progress meter */ | |
56 | static int status_pass=1; | |
57 | static int status_dots_requested=0; | |
58 | static int status_dots=1; | |
59 | ||
60 | void | |
61 | read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) | |
62 | { | |
63 | if(png_ptr == NULL || row_number > 0x3fffffffL) return; | |
64 | if(status_pass != pass) | |
65 | { | |
66 | fprintf(stdout,"\n Pass %d: ",pass); | |
67 | status_pass = pass; | |
68 | status_dots = 30; | |
69 | } | |
70 | status_dots--; | |
71 | if(status_dots == 0) | |
72 | { | |
73 | fprintf(stdout, "\n "); | |
74 | status_dots=30; | |
75 | } | |
76 | fprintf(stdout, "r"); | |
77 | } | |
78 | ||
79 | void | |
80 | write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) | |
81 | { | |
82 | if(png_ptr == NULL || row_number > 0x3fffffffL || pass > 7) return; | |
83 | fprintf(stdout, "w"); | |
84 | } | |
85 | ||
86 | ||
87 | #if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) | |
88 | /* example of using user transform callback (we don't transform anything, | |
89 | but merely count the zero samples) */ | |
90 | ||
91 | static png_uint_32 zero_samples; | |
92 | ||
93 | void | |
94 | count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data) | |
95 | { | |
96 | png_bytep dp = data; | |
97 | if(png_ptr == NULL)return; | |
98 | ||
99 | /* contents of row_info: | |
100 | * png_uint_32 width width of row | |
101 | * png_uint_32 rowbytes number of bytes in row | |
102 | * png_byte color_type color type of pixels | |
103 | * png_byte bit_depth bit depth of samples | |
104 | * png_byte channels number of channels (1-4) | |
105 | * png_byte pixel_depth bits per pixel (depth*channels) | |
106 | */ | |
107 | ||
108 | /* counts the number of zero samples (or zero pixels if color_type is 3 */ | |
109 | ||
110 | if(row_info->color_type == 0 || row_info->color_type == 3) | |
111 | { | |
112 | int pos=0; | |
113 | png_uint_32 n, nstop; | |
114 | for (n=0, nstop=row_info->width; n<nstop; n++) | |
115 | { | |
116 | if(row_info->bit_depth == 1) | |
117 | { | |
118 | if(((*dp << pos++ )& 0x80) == 0) zero_samples++; | |
119 | if(pos == 8) | |
120 | { | |
121 | pos = 0; | |
122 | dp++; | |
123 | } | |
124 | } | |
125 | if(row_info->bit_depth == 2) | |
126 | { | |
127 | if(((*dp << (pos+=2))& 0xc0) == 0) zero_samples++; | |
128 | if(pos == 8) | |
129 | { | |
130 | pos = 0; | |
131 | dp++; | |
132 | } | |
133 | } | |
134 | if(row_info->bit_depth == 4) | |
135 | { | |
136 | if(((*dp << (pos+=4))& 0xf0) == 0) zero_samples++; | |
137 | if(pos == 8) | |
138 | { | |
139 | pos = 0; | |
140 | dp++; | |
141 | } | |
142 | } | |
143 | if(row_info->bit_depth == 8) | |
144 | if(*dp++ == 0) zero_samples++; | |
145 | if(row_info->bit_depth == 16) | |
146 | { | |
147 | if((*dp | *(dp+1)) == 0) zero_samples++; | |
148 | dp+=2; | |
149 | } | |
150 | } | |
151 | } | |
152 | else /* other color types */ | |
153 | { | |
154 | png_uint_32 n, nstop; | |
155 | int channel; | |
156 | int color_channels = row_info->channels; | |
157 | if(row_info->color_type > 3)color_channels--; | |
158 | ||
159 | for (n=0, nstop=row_info->width; n<nstop; n++) | |
160 | { | |
161 | for (channel = 0; channel < color_channels; channel++) | |
162 | { | |
163 | if(row_info->bit_depth == 8) | |
164 | if(*dp++ == 0) zero_samples++; | |
165 | if(row_info->bit_depth == 16) | |
166 | { | |
167 | if((*dp | *(dp+1)) == 0) zero_samples++; | |
168 | dp+=2; | |
169 | } | |
170 | } | |
171 | if(row_info->color_type > 3) | |
172 | { | |
173 | dp++; | |
174 | if(row_info->bit_depth == 16)dp++; | |
175 | } | |
176 | } | |
177 | } | |
178 | } | |
179 | #endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */ | |
180 | ||
181 | static int verbose = 0; | |
182 | static int wrote_question = 0; | |
183 | ||
184 | #if defined(PNG_NO_STDIO) | |
185 | /* START of code to validate stdio-free compilation */ | |
186 | /* These copies of the default read/write functions come from pngrio.c and */ | |
187 | /* pngwio.c. They allow "don't include stdio" testing of the library. */ | |
188 | /* This is the function that does the actual reading of data. If you are | |
189 | not reading from a standard C stream, you should create a replacement | |
190 | read_data function and use it at run time with png_set_read_fn(), rather | |
191 | than changing the library. */ | |
192 | #ifndef USE_FAR_KEYWORD | |
193 | static void | |
194 | png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) | |
195 | { | |
196 | png_size_t check; | |
197 | ||
198 | /* fread() returns 0 on error, so it is OK to store this in a png_size_t | |
199 | * instead of an int, which is what fread() actually returns. | |
200 | */ | |
201 | check = (png_size_t)fread(data, (png_size_t)1, length, | |
202 | (FILE *)png_ptr->io_ptr); | |
203 | ||
204 | if (check != length) | |
205 | { | |
206 | png_error(png_ptr, "Read Error"); | |
207 | } | |
208 | } | |
209 | #else | |
210 | /* this is the model-independent version. Since the standard I/O library | |
211 | can't handle far buffers in the medium and small models, we have to copy | |
212 | the data. | |
213 | */ | |
214 | ||
215 | #define NEAR_BUF_SIZE 1024 | |
216 | #define MIN(a,b) (a <= b ? a : b) | |
217 | ||
218 | static void | |
219 | png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) | |
220 | { | |
221 | int check; | |
222 | png_byte *n_data; | |
223 | FILE *io_ptr; | |
224 | ||
225 | /* Check if data really is near. If so, use usual code. */ | |
226 | n_data = (png_byte *)CVT_PTR_NOCHECK(data); | |
227 | io_ptr = (FILE *)CVT_PTR(png_ptr->io_ptr); | |
228 | if ((png_bytep)n_data == data) | |
229 | { | |
230 | check = fread(n_data, 1, length, io_ptr); | |
231 | } | |
232 | else | |
233 | { | |
234 | png_byte buf[NEAR_BUF_SIZE]; | |
235 | png_size_t read, remaining, err; | |
236 | check = 0; | |
237 | remaining = length; | |
238 | do | |
239 | { | |
240 | read = MIN(NEAR_BUF_SIZE, remaining); | |
241 | err = fread(buf, (png_size_t)1, read, io_ptr); | |
242 | png_memcpy(data, buf, read); /* copy far buffer to near buffer */ | |
243 | if(err != read) | |
244 | break; | |
245 | else | |
246 | check += err; | |
247 | data += read; | |
248 | remaining -= read; | |
249 | } | |
250 | while (remaining != 0); | |
251 | } | |
252 | if (check != length) | |
253 | { | |
254 | png_error(png_ptr, "read Error"); | |
255 | } | |
256 | } | |
257 | #endif /* USE_FAR_KEYWORD */ | |
258 | ||
259 | #if defined(PNG_WRITE_FLUSH_SUPPORTED) | |
260 | static void | |
261 | png_default_flush(png_structp png_ptr) | |
262 | { | |
263 | FILE *io_ptr; | |
264 | io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr)); | |
265 | if (io_ptr != NULL) | |
266 | fflush(io_ptr); | |
267 | } | |
268 | #endif | |
269 | ||
270 | /* This is the function that does the actual writing of data. If you are | |
271 | not writing to a standard C stream, you should create a replacement | |
272 | write_data function and use it at run time with png_set_write_fn(), rather | |
273 | than changing the library. */ | |
274 | #ifndef USE_FAR_KEYWORD | |
275 | static void | |
276 | png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) | |
277 | { | |
278 | png_uint_32 check; | |
279 | ||
280 | check = fwrite(data, 1, length, (FILE *)(png_ptr->io_ptr)); | |
281 | if (check != length) | |
282 | { | |
283 | png_error(png_ptr, "Write Error"); | |
284 | } | |
285 | } | |
286 | #else | |
287 | /* this is the model-independent version. Since the standard I/O library | |
288 | can't handle far buffers in the medium and small models, we have to copy | |
289 | the data. | |
290 | */ | |
291 | ||
292 | #define NEAR_BUF_SIZE 1024 | |
293 | #define MIN(a,b) (a <= b ? a : b) | |
294 | ||
295 | static void | |
296 | png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) | |
297 | { | |
298 | png_uint_32 check; | |
299 | png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ | |
300 | FILE *io_ptr; | |
301 | ||
302 | /* Check if data really is near. If so, use usual code. */ | |
303 | near_data = (png_byte *)CVT_PTR_NOCHECK(data); | |
304 | io_ptr = (FILE *)CVT_PTR(png_ptr->io_ptr); | |
305 | if ((png_bytep)near_data == data) | |
306 | { | |
307 | check = fwrite(near_data, 1, length, io_ptr); | |
308 | } | |
309 | else | |
310 | { | |
311 | png_byte buf[NEAR_BUF_SIZE]; | |
312 | png_size_t written, remaining, err; | |
313 | check = 0; | |
314 | remaining = length; | |
315 | do | |
316 | { | |
317 | written = MIN(NEAR_BUF_SIZE, remaining); | |
318 | png_memcpy(buf, data, written); /* copy far buffer to near buffer */ | |
319 | err = fwrite(buf, 1, written, io_ptr); | |
320 | if (err != written) | |
321 | break; | |
322 | else | |
323 | check += err; | |
324 | data += written; | |
325 | remaining -= written; | |
326 | } | |
327 | while (remaining != 0); | |
328 | } | |
329 | if (check != length) | |
330 | { | |
331 | png_error(png_ptr, "Write Error"); | |
332 | } | |
333 | } | |
334 | ||
335 | #endif /* USE_FAR_KEYWORD */ | |
336 | ||
337 | /* This function is called when there is a warning, but the library thinks | |
338 | * it can continue anyway. Replacement functions don't have to do anything | |
339 | * here if you don't want to. In the default configuration, png_ptr is | |
340 | * not used, but it is passed in case it may be useful. | |
341 | */ | |
342 | static void | |
343 | png_default_warning(png_structp png_ptr, png_const_charp message) | |
344 | { | |
345 | PNG_CONST char *name = "UNKNOWN (ERROR!)"; | |
346 | if (png_ptr != NULL && png_ptr->error_ptr != NULL) | |
347 | name = png_ptr->error_ptr; | |
348 | fprintf(STDERR, "%s: libpng warning: %s\n", name, message); | |
349 | } | |
350 | ||
351 | /* This is the default error handling function. Note that replacements for | |
352 | * this function MUST NOT RETURN, or the program will likely crash. This | |
353 | * function is used by default, or if the program supplies NULL for the | |
354 | * error function pointer in png_set_error_fn(). | |
355 | */ | |
356 | static void | |
357 | png_default_error(png_structp png_ptr, png_const_charp message) | |
358 | { | |
359 | png_default_warning(png_ptr, message); | |
360 | /* We can return because png_error calls the default handler, which is | |
361 | * actually OK in this case. */ | |
362 | } | |
363 | #endif /* PNG_NO_STDIO */ | |
364 | /* END of code to validate stdio-free compilation */ | |
365 | ||
366 | /* START of code to validate memory allocation and deallocation */ | |
367 | #ifdef PNG_USER_MEM_SUPPORTED | |
368 | ||
369 | /* Allocate memory. For reasonable files, size should never exceed | |
370 | 64K. However, zlib may allocate more then 64K if you don't tell | |
371 | it not to. See zconf.h and png.h for more information. zlib does | |
372 | need to allocate exactly 64K, so whatever you call here must | |
373 | have the ability to do that. | |
374 | ||
375 | This piece of code can be compiled to validate max 64K allocations | |
376 | by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. */ | |
377 | typedef struct memory_information { | |
378 | png_uint_32 size; | |
379 | png_voidp pointer; | |
380 | struct memory_information FAR *next; | |
381 | } memory_information; | |
382 | typedef memory_information FAR *memory_infop; | |
383 | ||
384 | static memory_infop pinformation = NULL; | |
385 | static int current_allocation = 0; | |
386 | static int maximum_allocation = 0; | |
387 | ||
388 | extern PNG_EXPORT(png_voidp,png_debug_malloc) PNGARG((png_structp png_ptr, | |
389 | png_uint_32 size)); | |
390 | extern PNG_EXPORT(void,png_debug_free) PNGARG((png_structp png_ptr, | |
391 | png_voidp ptr)); | |
392 | ||
393 | png_voidp | |
394 | png_debug_malloc(png_structp png_ptr, png_uint_32 size) { | |
395 | ||
396 | /* png_malloc has already tested for NULL; png_create_struct calls | |
397 | png_debug_malloc directly, with png_ptr == NULL which is OK */ | |
398 | ||
399 | if (size == 0) | |
400 | return (png_voidp)(NULL); | |
401 | ||
402 | /* This calls the library allocator twice, once to get the requested | |
403 | buffer and once to get a new free list entry. */ | |
404 | { | |
405 | memory_infop pinfo = png_malloc_default(png_ptr, sizeof *pinfo); | |
406 | pinfo->size = size; | |
407 | current_allocation += size; | |
408 | if (current_allocation > maximum_allocation) | |
409 | maximum_allocation = current_allocation; | |
410 | pinfo->pointer = png_malloc_default(png_ptr, size); | |
411 | pinfo->next = pinformation; | |
412 | pinformation = pinfo; | |
413 | /* Make sure the caller isn't assuming zeroed memory. */ | |
414 | png_memset(pinfo->pointer, 0xdd, pinfo->size); | |
415 | return (png_voidp)(pinfo->pointer); | |
416 | } | |
417 | } | |
418 | ||
419 | /* Free a pointer. It is removed from the list at the same time. */ | |
420 | void | |
421 | png_debug_free(png_structp png_ptr, png_voidp ptr) | |
422 | { | |
423 | if (png_ptr == NULL) | |
424 | fprintf(STDERR, "NULL pointer to png_debug_free.\n"); | |
425 | if (ptr == 0) { | |
426 | #if 0 /* This happens all the time. */ | |
427 | fprintf(STDERR, "WARNING: freeing NULL pointer\n"); | |
428 | #endif | |
429 | return; | |
430 | } | |
431 | ||
432 | /* Unlink the element from the list. */ | |
433 | { | |
434 | memory_infop FAR *ppinfo = &pinformation; | |
435 | for (;;) { | |
436 | memory_infop pinfo = *ppinfo; | |
437 | if (pinfo->pointer == ptr) { | |
438 | *ppinfo = pinfo->next; | |
439 | current_allocation -= pinfo->size; | |
440 | if (current_allocation < 0) | |
441 | fprintf(STDERR, "Duplicate free of memory\n"); | |
442 | /* We must free the list element too, but first kill | |
443 | the memory that is to be freed. */ | |
444 | memset(ptr, 0x55, pinfo->size); | |
445 | png_free_default(png_ptr, pinfo); | |
446 | break; | |
447 | } | |
448 | if (pinfo->next == NULL) { | |
449 | fprintf(STDERR, "Pointer %x not found\n", ptr); | |
450 | break; | |
451 | } | |
452 | ppinfo = &pinfo->next; | |
453 | } | |
454 | } | |
455 | ||
456 | /* Finally free the data. */ | |
457 | png_free_default(png_ptr, ptr); | |
458 | } | |
459 | #endif /* PNG_USER_MEM_SUPPORTED */ | |
460 | /* END of code to test memory allocation/deallocation */ | |
461 | ||
462 | /* Test one file */ | |
463 | int | |
464 | test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) | |
465 | { | |
466 | static FILE *fpin, *fpout; /* "static" prevents setjmp corruption */ | |
467 | png_structp read_ptr, write_ptr; | |
468 | png_infop read_info_ptr, write_info_ptr, end_info_ptr; | |
469 | png_bytep row_buf; | |
470 | png_uint_32 y; | |
471 | png_uint_32 width, height; | |
472 | int num_pass, pass; | |
473 | int bit_depth, color_type; | |
474 | #ifdef USE_FAR_KEYWORD | |
475 | jmp_buf jmpbuf; | |
476 | #endif | |
477 | ||
478 | char inbuf[256], outbuf[256]; | |
479 | ||
480 | row_buf = (png_bytep)NULL; | |
481 | ||
482 | if ((fpin = fopen(inname, "rb")) == NULL) | |
483 | { | |
484 | fprintf(STDERR, "Could not find input file %s\n", inname); | |
485 | return (1); | |
486 | } | |
487 | ||
488 | if ((fpout = fopen(outname, "wb")) == NULL) | |
489 | { | |
490 | fprintf(STDERR, "Could not open output file %s\n", outname); | |
491 | fclose(fpin); | |
492 | return (1); | |
493 | } | |
494 | ||
495 | png_debug(0, "Allocating read and write structures\n"); | |
496 | #ifdef PNG_USER_MEM_SUPPORTED | |
497 | read_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, | |
498 | (png_error_ptr)NULL, (png_error_ptr)NULL, (png_voidp)NULL, | |
499 | (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); | |
500 | #else | |
501 | read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, | |
502 | (png_error_ptr)NULL, (png_error_ptr)NULL); | |
503 | #endif | |
504 | #if defined(PNG_NO_STDIO) | |
505 | png_set_error_fn(read_ptr, (png_voidp)inname, png_default_error, | |
506 | png_default_warning); | |
507 | #endif | |
508 | #ifdef PNG_USER_MEM_SUPPORTED | |
509 | write_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, | |
510 | (png_error_ptr)NULL, (png_error_ptr)NULL, (png_voidp)NULL, | |
511 | (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); | |
512 | #else | |
513 | write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, | |
514 | (png_error_ptr)NULL, (png_error_ptr)NULL); | |
515 | #endif | |
516 | #if defined(PNG_NO_STDIO) | |
517 | png_set_error_fn(write_ptr, (png_voidp)inname, png_default_error, | |
518 | png_default_warning); | |
519 | #endif | |
520 | png_debug(0, "Allocating read_info, write_info and end_info structures\n"); | |
521 | read_info_ptr = png_create_info_struct(read_ptr); | |
522 | write_info_ptr = png_create_info_struct(write_ptr); | |
523 | end_info_ptr = png_create_info_struct(read_ptr); | |
524 | #ifdef PNG_USER_MEM_SUPPORTED | |
525 | #endif | |
526 | ||
527 | png_debug(0, "Setting jmpbuf for read struct\n"); | |
528 | #ifdef USE_FAR_KEYWORD | |
529 | if (setjmp(jmpbuf)) | |
530 | #else | |
531 | if (setjmp(read_ptr->jmpbuf)) | |
532 | #endif | |
533 | { | |
534 | fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname); | |
535 | png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); | |
536 | png_destroy_write_struct(&write_ptr, &write_info_ptr); | |
537 | fclose(fpin); | |
538 | fclose(fpout); | |
539 | return (1); | |
540 | } | |
541 | #ifdef USE_FAR_KEYWORD | |
542 | png_memcpy(read_ptr->jmpbuf,jmpbuf,sizeof(jmp_buf)); | |
543 | #endif | |
544 | ||
545 | png_debug(0, "Setting jmpbuf for write struct\n"); | |
546 | #ifdef USE_FAR_KEYWORD | |
547 | if (setjmp(jmpbuf)) | |
548 | #else | |
549 | if (setjmp(write_ptr->jmpbuf)) | |
550 | #endif | |
551 | { | |
552 | fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname); | |
553 | png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); | |
554 | png_destroy_write_struct(&write_ptr, &write_info_ptr); | |
555 | fclose(fpin); | |
556 | fclose(fpout); | |
557 | return (1); | |
558 | } | |
559 | #ifdef USE_FAR_KEYWORD | |
560 | png_memcpy(write_ptr->jmpbuf,jmpbuf,sizeof(jmp_buf)); | |
561 | #endif | |
562 | ||
563 | png_debug(0, "Initializing input and output streams\n"); | |
564 | #if !defined(PNG_NO_STDIO) | |
565 | png_init_io(read_ptr, fpin); | |
566 | png_init_io(write_ptr, fpout); | |
567 | #else | |
568 | png_set_read_fn(read_ptr, (png_voidp)fpin, png_default_read_data); | |
569 | png_set_write_fn(write_ptr, (png_voidp)fpout, png_default_write_data, | |
570 | #if defined(PNG_WRITE_FLUSH_SUPPORTED) | |
571 | png_default_flush); | |
572 | #else | |
573 | NULL); | |
574 | #endif | |
575 | #endif | |
576 | if(status_dots_requested == 1) | |
577 | { | |
578 | png_set_write_status_fn(write_ptr, write_row_callback); | |
579 | png_set_read_status_fn(read_ptr, read_row_callback); | |
580 | } | |
581 | else | |
582 | { | |
583 | png_set_write_status_fn(write_ptr, NULL); | |
584 | png_set_read_status_fn(read_ptr, NULL); | |
585 | } | |
586 | ||
587 | # if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) | |
588 | zero_samples=0; | |
589 | png_set_write_user_transform_fn(write_ptr, count_zero_samples); | |
590 | # endif | |
591 | ||
592 | png_debug(0, "Reading info struct\n"); | |
593 | png_read_info(read_ptr, read_info_ptr); | |
594 | ||
595 | png_debug(0, "Transferring info struct\n"); | |
596 | { | |
597 | int interlace_type, compression_type, filter_type; | |
598 | ||
599 | if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth, | |
600 | &color_type, &interlace_type, &compression_type, &filter_type)) | |
601 | { | |
602 | png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, | |
603 | #if defined(PNG_WRITE_INTERLACING_SUPPORTED) | |
604 | color_type, interlace_type, compression_type, filter_type); | |
605 | #else | |
606 | color_type, PNG_INTERLACE_NONE, compression_type, filter_type); | |
607 | #endif | |
608 | } | |
609 | } | |
610 | #if defined(PNG_READ_bKGD_SUPPORTED) && defined(PNG_WRITE_bKGD_SUPPORTED) | |
611 | { | |
612 | png_color_16p background; | |
613 | ||
614 | if (png_get_bKGD(read_ptr, read_info_ptr, &background)) | |
615 | { | |
616 | png_set_bKGD(write_ptr, write_info_ptr, background); | |
617 | } | |
618 | } | |
619 | #endif | |
620 | #if defined(PNG_READ_cHRM_SUPPORTED) && defined(PNG_WRITE_cHRM_SUPPORTED) | |
621 | { | |
622 | double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; | |
623 | ||
624 | if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, | |
625 | &red_y, &green_x, &green_y, &blue_x, &blue_y)) | |
626 | { | |
627 | png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x, | |
628 | red_y, green_x, green_y, blue_x, blue_y); | |
629 | } | |
630 | } | |
631 | #endif | |
632 | #if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_WRITE_gAMA_SUPPORTED) | |
633 | { | |
634 | double gamma; | |
635 | ||
636 | if (png_get_gAMA(read_ptr, read_info_ptr, &gamma)) | |
637 | { | |
638 | png_set_gAMA(write_ptr, write_info_ptr, gamma); | |
639 | } | |
640 | } | |
641 | #endif | |
642 | #if defined(PNG_READ_sRGB_SUPPORTED) && defined(PNG_WRITE_sRGB_SUPPORTED) | |
643 | { | |
644 | int intent; | |
645 | ||
646 | if (png_get_sRGB(read_ptr, read_info_ptr, &intent)) | |
647 | { | |
648 | png_set_sRGB(write_ptr, write_info_ptr, intent); | |
649 | } | |
650 | } | |
651 | #endif | |
652 | #if defined(PNG_READ_hIST_SUPPORTED) && defined(PNG_WRITE_hIST_SUPPORTED) | |
653 | { | |
654 | png_uint_16p hist; | |
655 | ||
656 | if (png_get_hIST(read_ptr, read_info_ptr, &hist)) | |
657 | { | |
658 | png_set_hIST(write_ptr, write_info_ptr, hist); | |
659 | } | |
660 | } | |
661 | #endif | |
662 | #if defined(PNG_READ_oFFs_SUPPORTED) && defined(PNG_WRITE_oFFs_SUPPORTED) | |
663 | { | |
664 | png_uint_32 offset_x, offset_y; | |
665 | int unit_type; | |
666 | ||
667 | if (png_get_oFFs(read_ptr, read_info_ptr,&offset_x,&offset_y,&unit_type)) | |
668 | { | |
669 | png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type); | |
670 | } | |
671 | } | |
672 | #endif | |
673 | #if defined(PNG_READ_pCAL_SUPPORTED) && defined(PNG_WRITE_pCAL_SUPPORTED) | |
674 | { | |
675 | png_charp purpose, units; | |
676 | png_charpp params; | |
677 | png_int_32 X0, X1; | |
678 | int type, nparams; | |
679 | ||
680 | if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type, | |
681 | &nparams, &units, ¶ms)) | |
682 | { | |
683 | png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type, | |
684 | nparams, units, params); | |
685 | } | |
686 | } | |
687 | #endif | |
688 | #if defined(PNG_READ_pHYs_SUPPORTED) && defined(PNG_WRITE_pHYs_SUPPORTED) | |
689 | { | |
690 | png_uint_32 res_x, res_y; | |
691 | int unit_type; | |
692 | ||
693 | if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type)) | |
694 | { | |
695 | png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type); | |
696 | } | |
697 | } | |
698 | #endif | |
699 | { | |
700 | png_colorp palette; | |
701 | int num_palette; | |
702 | ||
703 | if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette)) | |
704 | { | |
705 | png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette); | |
706 | } | |
707 | } | |
708 | #if defined(PNG_READ_sBIT_SUPPORTED) && defined(PNG_WRITE_sBIT_SUPPORTED) | |
709 | { | |
710 | png_color_8p sig_bit; | |
711 | ||
712 | if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit)) | |
713 | { | |
714 | png_set_sBIT(write_ptr, write_info_ptr, sig_bit); | |
715 | } | |
716 | } | |
717 | #endif | |
718 | #if (defined(PNG_READ_tEXt_SUPPORTED) && defined(PNG_WRITE_tEXt_SUPPORTED)) || \ | |
719 | (defined(PNG_READ_zTXt_SUPPORTED) && defined(PNG_WRITE_zTXt_SUPPORTED)) | |
720 | { | |
721 | png_textp text_ptr; | |
722 | int num_text; | |
723 | ||
724 | if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0) | |
725 | { | |
726 | png_debug1(0, "Handling %d tEXt/zTXt chunks\n", num_text); | |
727 | png_set_text(write_ptr, write_info_ptr, text_ptr, num_text); | |
728 | } | |
729 | } | |
730 | #endif | |
731 | #if defined(PNG_READ_tIME_SUPPORTED) && defined(PNG_WRITE_tIME_SUPPORTED) | |
732 | { | |
733 | png_timep mod_time; | |
734 | ||
735 | if (png_get_tIME(read_ptr, read_info_ptr, &mod_time)) | |
736 | { | |
737 | png_set_tIME(write_ptr, write_info_ptr, mod_time); | |
738 | #if defined(PNG_TIME_RFC1123_SUPPORTED) | |
739 | /* we have to use png_strcpy instead of "=" because the string | |
740 | pointed to by png_convert_to_rfc1123() gets free'ed before | |
741 | we use it */ | |
742 | png_strcpy(tIME_string,png_convert_to_rfc1123(read_ptr, mod_time)); | |
743 | tIME_chunk_present++; | |
744 | #endif /* PNG_TIME_RFC1123_SUPPORTED */ | |
745 | } | |
746 | } | |
747 | #endif | |
748 | #if defined(PNG_READ_tRNS_SUPPORTED) && defined(PNG_WRITE_tRNS_SUPPORTED) | |
749 | { | |
750 | png_bytep trans; | |
751 | int num_trans; | |
752 | png_color_16p trans_values; | |
753 | ||
754 | if (png_get_tRNS(read_ptr, read_info_ptr, &trans, &num_trans, | |
755 | &trans_values)) | |
756 | { | |
757 | png_set_tRNS(write_ptr, write_info_ptr, trans, num_trans, | |
758 | trans_values); | |
759 | } | |
760 | } | |
761 | #endif | |
762 | ||
763 | png_debug(0, "\nWriting info struct\n"); | |
764 | png_write_info(write_ptr, write_info_ptr); | |
765 | ||
766 | png_debug(0, "\nAllocating row buffer \n"); | |
767 | row_buf = (png_bytep)png_malloc(read_ptr, | |
768 | png_get_rowbytes(read_ptr, read_info_ptr)); | |
769 | if (row_buf == NULL) | |
770 | { | |
771 | fprintf(STDERR, "No memory to allocate row buffer\n"); | |
772 | png_destroy_read_struct(&read_ptr, &read_info_ptr, (png_infopp)NULL); | |
773 | png_destroy_write_struct(&write_ptr, &write_info_ptr); | |
774 | fclose(fpin); | |
775 | fclose(fpout); | |
776 | return (1); | |
777 | } | |
778 | png_debug(0, "Writing row data\n"); | |
779 | ||
780 | num_pass = png_set_interlace_handling(read_ptr); | |
781 | png_set_interlace_handling(write_ptr); | |
782 | ||
783 | for (pass = 0; pass < num_pass; pass++) | |
784 | { | |
785 | png_debug1(0, "Writing row data for pass %d\n",pass); | |
786 | for (y = 0; y < height; y++) | |
787 | { | |
788 | png_read_rows(read_ptr, (png_bytepp)&row_buf, (png_bytepp)NULL, 1); | |
789 | png_write_rows(write_ptr, (png_bytepp)&row_buf, 1); | |
790 | } | |
791 | } | |
792 | ||
793 | png_debug(0, "Reading and writing end_info data\n"); | |
794 | png_read_end(read_ptr, end_info_ptr); | |
795 | png_write_end(write_ptr, end_info_ptr); | |
796 | ||
797 | #ifdef PNG_EASY_ACCESS_SUPPORTED | |
798 | if(verbose) | |
799 | { | |
800 | png_uint_32 iwidth, iheight; | |
801 | iwidth = png_get_image_width(write_ptr, write_info_ptr); | |
802 | iheight = png_get_image_height(write_ptr, write_info_ptr); | |
803 | fprintf(STDERR, "Image width = %lu, height = %lu\n", | |
804 | iwidth, iheight); | |
805 | } | |
806 | #endif | |
807 | ||
808 | png_debug(0, "Destroying data structs\n"); | |
809 | png_free(read_ptr, row_buf); | |
810 | png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); | |
811 | png_destroy_write_struct(&write_ptr, &write_info_ptr); | |
812 | ||
813 | fclose(fpin); | |
814 | fclose(fpout); | |
815 | ||
816 | png_debug(0, "Opening files for comparison\n"); | |
817 | if ((fpin = fopen(inname, "rb")) == NULL) | |
818 | { | |
819 | fprintf(STDERR, "Could not find file %s\n", inname); | |
820 | return (1); | |
821 | } | |
822 | ||
823 | if ((fpout = fopen(outname, "rb")) == NULL) | |
824 | { | |
825 | fprintf(STDERR, "Could not find file %s\n", outname); | |
826 | fclose(fpin); | |
827 | return (1); | |
828 | } | |
829 | ||
830 | for(;;) | |
831 | { | |
832 | png_size_t num_in, num_out; | |
833 | ||
834 | num_in = fread(inbuf, 1, 1, fpin); | |
835 | num_out = fread(outbuf, 1, 1, fpout); | |
836 | ||
837 | if (num_in != num_out) | |
838 | { | |
839 | fprintf(STDERR, "Files %s and %s are of a different size\n", | |
840 | inname, outname); | |
841 | if(wrote_question == 0) | |
842 | { | |
843 | fprintf(STDERR, | |
844 | " Was %s written with the same chunk size (8k),",inname); | |
845 | fprintf(STDERR, | |
846 | " filtering\n heuristic (libpng default), compression"); | |
847 | fprintf(STDERR, | |
848 | " level (zlib default)\n and zlib version (%s)?\n\n", | |
849 | ZLIB_VERSION); | |
850 | wrote_question=1; | |
851 | } | |
852 | fclose(fpin); | |
853 | fclose(fpout); | |
854 | return (0); | |
855 | } | |
856 | ||
857 | if (!num_in) | |
858 | break; | |
859 | ||
860 | if (png_memcmp(inbuf, outbuf, num_in)) | |
861 | { | |
862 | fprintf(STDERR, "Files %s and %s are different\n", inname, outname); | |
863 | if(wrote_question == 0) | |
864 | { | |
865 | fprintf(STDERR, | |
866 | " Was %s written with the same chunk size (8k),",inname); | |
867 | fprintf(STDERR, | |
868 | " filtering\n heuristic (libpng default), compression"); | |
869 | fprintf(STDERR, | |
870 | " level (zlib default)\n and zlib version (%s)?\n\n", | |
871 | ZLIB_VERSION); | |
872 | wrote_question=1; | |
873 | } | |
874 | fclose(fpin); | |
875 | fclose(fpout); | |
876 | return (0); | |
877 | } | |
878 | } | |
879 | ||
880 | fclose(fpin); | |
881 | fclose(fpout); | |
882 | ||
883 | return (0); | |
884 | } | |
885 | ||
886 | /* input and output filenames */ | |
887 | #ifdef RISCOS | |
888 | PNG_CONST char *inname = "pngtest/png"; | |
889 | PNG_CONST char *outname = "pngout/png"; | |
890 | #else | |
891 | static PNG_CONST char *inname = "pngtest.png"; | |
892 | static PNG_CONST char *outname = "pngout.png"; | |
893 | #endif | |
894 | ||
895 | int | |
896 | main(int argc, char *argv[]) | |
897 | { | |
898 | int multiple = 0; | |
899 | int ierror = 0; | |
900 | ||
901 | fprintf(STDERR, "Testing libpng version %s\n", PNG_LIBPNG_VER_STRING); | |
902 | fprintf(STDERR, " with zlib version %s\n", ZLIB_VERSION); | |
903 | fprintf(STDERR,"%s",png_get_copyright(NULL)); | |
904 | ||
905 | /* Do some consistency checking on the memory allocation settings, I'm | |
906 | not sure this matters, but it is nice to know, the first of these | |
907 | tests should be impossible because of the way the macros are set | |
908 | in pngconf.h */ | |
909 | #if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) | |
910 | fprintf(STDERR, " NOTE: Zlib compiled for max 64k, libpng not\n"); | |
911 | #endif | |
912 | /* I think the following can happen. */ | |
913 | #if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K) | |
914 | fprintf(STDERR, " NOTE: libpng compiled for max 64k, zlib not\n"); | |
915 | #endif | |
916 | ||
917 | if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) | |
918 | { | |
919 | fprintf(STDERR, | |
920 | "Warning: versions are different between png.h and png.c\n"); | |
921 | fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); | |
922 | fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); | |
923 | ++ierror; | |
924 | } | |
925 | ||
926 | if (argc > 1) | |
927 | { | |
928 | if (strcmp(argv[1], "-m") == 0) | |
929 | { | |
930 | multiple = 1; | |
931 | status_dots_requested = 0; | |
932 | } | |
933 | else if (strcmp(argv[1], "-mv") == 0 || | |
934 | strcmp(argv[1], "-vm") == 0 ) | |
935 | { | |
936 | multiple = 1; | |
937 | verbose = 1; | |
938 | status_dots_requested = 1; | |
939 | } | |
940 | else if (strcmp(argv[1], "-v") == 0) | |
941 | { | |
942 | verbose = 1; | |
943 | status_dots_requested = 1; | |
944 | inname = argv[2]; | |
945 | } | |
946 | else | |
947 | { | |
948 | inname = argv[1]; | |
949 | status_dots_requested = 0; | |
950 | } | |
951 | } | |
952 | ||
953 | if (!multiple && argc == 3+verbose) | |
954 | outname = argv[2+verbose]; | |
955 | ||
956 | if ((!multiple && argc > 3+verbose) || (multiple && argc < 2)) | |
957 | { | |
958 | fprintf(STDERR, | |
959 | "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n", | |
960 | argv[0], argv[0]); | |
961 | fprintf(STDERR, | |
962 | " reads/writes one PNG file (without -m) or multiple files (-m)\n"); | |
963 | fprintf(STDERR, | |
964 | " with -m %s is used as a temporary file\n", outname); | |
965 | exit(1); | |
966 | } | |
967 | ||
968 | if (multiple) | |
969 | { | |
970 | int i; | |
971 | #ifdef PNG_USER_MEM_SUPPORTED | |
972 | int allocation_now = current_allocation; | |
973 | #endif | |
974 | for (i=2; i<argc; ++i) | |
975 | { | |
976 | int kerror; | |
977 | fprintf(STDERR, "Testing %s:",argv[i]); | |
978 | kerror = test_one_file(argv[i], outname); | |
979 | if (kerror == 0) | |
980 | { | |
981 | #if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) | |
982 | fprintf(STDERR, " PASS (%lu zero samples)\n",zero_samples); | |
983 | #else | |
984 | fprintf(STDERR, " PASS\n"); | |
985 | #endif | |
986 | #if defined(PNG_TIME_RFC1123_SUPPORTED) | |
987 | if(tIME_chunk_present != 0) | |
988 | fprintf(STDERR, " tIME = %s\n",tIME_string); | |
989 | tIME_chunk_present = 0; | |
990 | #endif /* PNG_TIME_RFC1123_SUPPORTED */ | |
991 | } | |
992 | else | |
993 | { | |
994 | fprintf(STDERR, " FAIL\n"); | |
995 | ierror += kerror; | |
996 | } | |
997 | #ifdef PNG_USER_MEM_SUPPORTED | |
998 | if (allocation_now != current_allocation) | |
999 | fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", | |
1000 | current_allocation-allocation_now); | |
1001 | if (current_allocation != 0) { | |
1002 | memory_infop pinfo = pinformation; | |
1003 | ||
1004 | fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", | |
1005 | current_allocation); | |
1006 | while (pinfo != NULL) { | |
1007 | fprintf(STDERR, " %d bytes at %x\n", pinfo->size, pinfo->pointer); | |
1008 | pinfo = pinfo->next; | |
1009 | } | |
1010 | } | |
1011 | #endif | |
1012 | } | |
1013 | #ifdef PNG_USER_MEM_SUPPORTED | |
1014 | fprintf(STDERR, "Maximum memory allocation: %d bytes\n", | |
1015 | maximum_allocation); | |
1016 | #endif | |
1017 | } | |
1018 | else | |
1019 | { | |
1020 | int i; | |
1021 | for (i=0; i<3; ++i) { | |
1022 | int kerror; | |
1023 | #ifdef PNG_USER_MEM_SUPPORTED | |
1024 | int allocation_now = current_allocation; | |
1025 | #endif | |
1026 | if (i == 1) status_dots_requested = 1; | |
1027 | else if(verbose == 0)status_dots_requested = 0; | |
1028 | if (i == 0 || verbose == 1 || ierror != 0) | |
1029 | fprintf(STDERR, "Testing %s:",inname); | |
1030 | kerror = test_one_file(inname, outname); | |
1031 | if(kerror == 0) | |
1032 | { | |
1033 | if(verbose == 1 || i == 2) | |
1034 | { | |
1035 | #if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) | |
1036 | fprintf(STDERR, " PASS (%lu zero samples)\n",zero_samples); | |
1037 | #else | |
1038 | fprintf(STDERR, " PASS\n"); | |
1039 | #endif | |
1040 | #if defined(PNG_TIME_RFC1123_SUPPORTED) | |
1041 | if(tIME_chunk_present != 0) | |
1042 | fprintf(STDERR, " tIME = %s\n",tIME_string); | |
1043 | #endif /* PNG_TIME_RFC1123_SUPPORTED */ | |
1044 | } | |
1045 | } | |
1046 | else | |
1047 | { | |
1048 | if(verbose == 0 && i != 2) | |
1049 | fprintf(STDERR, "Testing %s:",inname); | |
1050 | fprintf(STDERR, " FAIL\n"); | |
1051 | ierror += kerror; | |
1052 | } | |
1053 | #ifdef PNG_USER_MEM_SUPPORTED | |
1054 | if (allocation_now != current_allocation) | |
1055 | fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", | |
1056 | current_allocation-allocation_now); | |
1057 | if (current_allocation != 0) { | |
1058 | memory_infop pinfo = pinformation; | |
1059 | ||
1060 | fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", | |
1061 | current_allocation); | |
1062 | while (pinfo != NULL) { | |
1063 | fprintf(STDERR, " %d bytes at %x\n", pinfo->size, pinfo->pointer); | |
1064 | pinfo = pinfo->next; | |
1065 | } | |
1066 | } | |
1067 | #endif | |
1068 | } | |
1069 | #ifdef PNG_USER_MEM_SUPPORTED | |
1070 | fprintf(STDERR, "Maximum memory allocation: %d bytes\n", | |
1071 | maximum_allocation); | |
1072 | #endif | |
1073 | } | |
1074 | ||
1075 | if (ierror == 0) | |
1076 | fprintf(STDERR, "libpng passes test\n"); | |
1077 | else | |
1078 | fprintf(STDERR, "libpng FAILS test\n"); | |
1079 | return (int)(ierror != 0); | |
1080 | } | |
1081 |