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