VirtualBox

source: vbox/trunk/src/libs/libpng-1.6.45/contrib/libtests/timepng.c@ 108378

最後變更 在這個檔案從108378是 107813,由 vboxsync 提交於 2 月 前

libpng-1.6.45: Applied and adjusted our libpng changes to 1.6.45. bugref:8515

  • 屬性 svn:eol-style 設為 native
檔案大小: 15.1 KB
 
1/* timepng.c
2 *
3 * Copyright (c) 2013,2016 John Cunningham Bowler
4 *
5 * This code is released under the libpng license.
6 * For conditions of distribution and use, see the disclaimer
7 * and license in png.h
8 *
9 * Load an arbitrary number of PNG files (from the command line, or, if there
10 * are no arguments on the command line, from stdin) then run a time test by
11 * reading each file by row or by image (possibly with transforms in the latter
12 * case). The only output is a time as a floating point number of seconds with
13 * 9 decimal digits.
14 */
15#define _POSIX_C_SOURCE 199309L /* for clock_gettime */
16
17#include <stdlib.h>
18#include <stdio.h>
19#include <string.h>
20#include <errno.h>
21#include <limits.h>
22
23#include <time.h>
24
25#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
26# include <config.h>
27#endif
28
29/* Define the following to use this test against your installed libpng, rather
30 * than the one being built here:
31 */
32#ifdef PNG_FREESTANDING_TESTS
33# include <png.h>
34#else
35# include "../../png.h"
36#endif
37
38/* The following is to support direct compilation of this file as C++ */
39#ifdef __cplusplus
40# define voidcast(type, value) static_cast<type>(value)
41#else
42# define voidcast(type, value) (value)
43#endif /* __cplusplus */
44
45/* 'CLOCK_PROCESS_CPUTIME_ID' is one of the clock timers for clock_gettime. It
46 * need not be supported even when clock_gettime is available. It returns the
47 * 'CPU' time the process has consumed. 'CPU' time is assumed to include time
48 * when the CPU is actually blocked by a pending cache fill but not time
49 * waiting for page faults. The attempt is to get a measure of the actual time
50 * the implementation takes to read a PNG ignoring the potentially very large IO
51 * overhead.
52 */
53#if defined (CLOCK_PROCESS_CPUTIME_ID) && defined(PNG_STDIO_SUPPORTED) &&\
54 defined(PNG_EASY_ACCESS_SUPPORTED) &&\
55 (PNG_LIBPNG_VER >= 10700 ? defined(PNG_READ_PNG_SUPPORTED) :\
56 defined (PNG_SEQUENTIAL_READ_SUPPORTED) &&\
57 defined(PNG_INFO_IMAGE_SUPPORTED))
58
59typedef struct
60{
61 FILE *input;
62 FILE *output;
63} io_data;
64
65static PNG_CALLBACK(void, read_and_copy,
66 (png_structp png_ptr, png_bytep buffer, size_t cb))
67{
68 io_data *io = (io_data*)png_get_io_ptr(png_ptr);
69
70 if (fread(buffer, cb, 1, io->input) != 1)
71 png_error(png_ptr, strerror(errno));
72
73 if (fwrite(buffer, cb, 1, io->output) != 1)
74 {
75 perror("temporary file");
76 fprintf(stderr, "temporary file PNG write failed\n");
77 exit(1);
78 }
79}
80
81static void read_by_row(png_structp png_ptr, png_infop info_ptr,
82 FILE *write_ptr, FILE *read_ptr)
83{
84 /* These don't get freed on error, this is fine; the program immediately
85 * exits.
86 */
87 png_bytep row = NULL, display = NULL;
88 io_data io_copy;
89
90 if (write_ptr != NULL)
91 {
92 /* Set up for a copy to the temporary file: */
93 io_copy.input = read_ptr;
94 io_copy.output = write_ptr;
95 png_set_read_fn(png_ptr, &io_copy, read_and_copy);
96 }
97
98 png_read_info(png_ptr, info_ptr);
99
100 {
101 size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
102
103 row = voidcast(png_bytep,malloc(rowbytes));
104 display = voidcast(png_bytep,malloc(rowbytes));
105
106 if (row == NULL || display == NULL)
107 png_error(png_ptr, "OOM allocating row buffers");
108
109 {
110 png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
111 int passes = png_set_interlace_handling(png_ptr);
112 int pass;
113
114 png_start_read_image(png_ptr);
115
116 for (pass = 0; pass < passes; ++pass)
117 {
118 png_uint_32 y = height;
119
120 /* NOTE: this trashes the row each time; interlace handling won't
121 * work, but this avoids memory thrashing for speed testing and is
122 * somewhat representative of an application that works row-by-row.
123 */
124 while (y-- > 0)
125 png_read_row(png_ptr, row, display);
126 }
127 }
128 }
129
130 /* Make sure to read to the end of the file: */
131 png_read_end(png_ptr, info_ptr);
132
133 /* Free this up: */
134 free(row);
135 free(display);
136}
137
138static PNG_CALLBACK(void, no_warnings, (png_structp png_ptr,
139 png_const_charp warning))
140{
141 (void)png_ptr;
142 (void)warning;
143}
144
145static int read_png(FILE *fp, png_int_32 transforms, FILE *write_file)
146{
147 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,
148 no_warnings);
149 png_infop info_ptr = NULL;
150
151 if (png_ptr == NULL)
152 return 0;
153
154 if (setjmp(png_jmpbuf(png_ptr)))
155 {
156 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
157 return 0;
158 }
159
160# ifdef PNG_BENIGN_ERRORS_SUPPORTED
161 png_set_benign_errors(png_ptr, 1/*allowed*/);
162# endif
163 png_init_io(png_ptr, fp);
164
165 info_ptr = png_create_info_struct(png_ptr);
166
167 if (info_ptr == NULL)
168 png_error(png_ptr, "OOM allocating info structure");
169
170 if (transforms < 0)
171 read_by_row(png_ptr, info_ptr, write_file, fp);
172
173 else
174 png_read_png(png_ptr, info_ptr, transforms, NULL/*params*/);
175
176 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
177 return 1;
178}
179
180static int mytime(struct timespec *t)
181{
182 /* Do the timing using clock_gettime and the per-process timer. */
183 if (!clock_gettime(CLOCK_PROCESS_CPUTIME_ID, t))
184 return 1;
185
186 perror("CLOCK_PROCESS_CPUTIME_ID");
187 fprintf(stderr, "timepng: could not get the time\n");
188 return 0;
189}
190
191static int perform_one_test(FILE *fp, int nfiles, png_int_32 transforms)
192{
193 int i;
194 struct timespec before, after;
195
196 /* Clear out all errors: */
197 rewind(fp);
198
199 if (mytime(&before))
200 {
201 for (i=0; i<nfiles; ++i)
202 {
203 if (read_png(fp, transforms, NULL/*write*/))
204 {
205 if (ferror(fp))
206 {
207 perror("temporary file");
208 fprintf(stderr, "file %d: error reading PNG data\n", i);
209 return 0;
210 }
211 }
212
213 else
214 {
215 perror("temporary file");
216 fprintf(stderr, "file %d: error from libpng\n", i);
217 return 0;
218 }
219 }
220 }
221
222 else
223 return 0;
224
225 if (mytime(&after))
226 {
227 /* Work out the time difference and print it - this is the only output,
228 * so flush it immediately.
229 */
230 unsigned long s = after.tv_sec - before.tv_sec;
231 long ns = after.tv_nsec - before.tv_nsec;
232
233 if (ns < 0)
234 {
235 --s;
236 ns += 1000000000;
237
238 if (ns < 0)
239 {
240 fprintf(stderr, "timepng: bad clock from kernel\n");
241 return 0;
242 }
243 }
244
245 printf("%lu.%.9ld\n", s, ns);
246 fflush(stdout);
247 if (ferror(stdout))
248 {
249 fprintf(stderr, "timepng: error writing output\n");
250 return 0;
251 }
252
253 /* Successful return */
254 return 1;
255 }
256
257 else
258 return 0;
259}
260
261static int add_one_file(FILE *fp, char *name)
262{
263 FILE *ip = fopen(name, "rb");
264
265 if (ip != NULL)
266 {
267 /* Read the file using libpng; this detects errors and also deals with
268 * files which contain data beyond the end of the file.
269 */
270 int ok = 0;
271 fpos_t pos;
272
273 if (fgetpos(fp, &pos))
274 {
275 /* Fatal error reading the start: */
276 perror("temporary file");
277 fprintf(stderr, "temporary file fgetpos error\n");
278 exit(1);
279 }
280
281 if (read_png(ip, -1/*by row*/, fp/*output*/))
282 {
283 if (ferror(ip))
284 {
285 perror(name);
286 fprintf(stderr, "%s: read error\n", name);
287 }
288
289 else
290 ok = 1; /* read ok */
291 }
292
293 else
294 fprintf(stderr, "%s: file not added\n", name);
295
296 (void)fclose(ip);
297
298 /* An error in the output is fatal; exit immediately: */
299 if (ferror(fp))
300 {
301 perror("temporary file");
302 fprintf(stderr, "temporary file write error\n");
303 exit(1);
304 }
305
306 if (ok)
307 return 1;
308
309 /* Did not read the file successfully, simply rewind the temporary
310 * file. This must happen after the ferror check above to avoid clearing
311 * the error.
312 */
313 if (fsetpos(fp, &pos))
314 {
315 perror("temporary file");
316 fprintf(stderr, "temporary file fsetpos error\n");
317 exit(1);
318 }
319 }
320
321 else
322 {
323 /* file open error: */
324 perror(name);
325 fprintf(stderr, "%s: open failed\n", name);
326 }
327
328 return 0; /* file not added */
329}
330
331static void
332usage(FILE *fp)
333{
334 if (fp != NULL) fclose(fp);
335
336 fprintf(stderr,
337"Usage:\n"
338" timepng --assemble <assembly> {files}\n"
339" Read the files into <assembly>, output the count. Options are ignored.\n"
340" timepng --dissemble <assembly> <count> [options]\n"
341" Time <count> files from <assembly>, additional files may not be given.\n"
342" Otherwise:\n"
343" Read the files into a temporary file and time the decode\n"
344"Transforms:\n"
345" --by-image: read by image with png_read_png\n"
346" --<transform>: implies by-image, use PNG_TRANSFORM_<transform>\n"
347" Otherwise: read by row using png_read_row (to a single row buffer)\n"
348 /* ISO C90 string length max 509 */);fprintf(stderr,
349"{files}:\n"
350" PNG files to copy into the assembly and time. Invalid files are skipped\n"
351" with appropriate error messages. If no files are given the list of files\n"
352" is read from stdin with each file name terminated by a newline\n"
353"Output:\n"
354" For --assemble the output is the name of the assembly file followed by the\n"
355" count of the files it contains; the arguments for --dissemble. Otherwise\n"
356" the output is the total decode time in seconds.\n");
357
358 exit(99);
359}
360
361int main(int argc, char **argv)
362{
363 int ok = 0;
364 int err = 0;
365 int nfiles = 0;
366 int transforms = -1; /* by row */
367 const char *assembly = NULL;
368 FILE *fp;
369
370 if (argc > 2 && strcmp(argv[1], "--assemble") == 0)
371 {
372 /* Just build the test file, argv[2] is the file name. */
373 assembly = argv[2];
374 fp = fopen(assembly, "wb");
375 if (fp == NULL)
376 {
377 perror(assembly);
378 fprintf(stderr, "timepng --assemble %s: could not open for write\n",
379 assembly);
380 usage(NULL);
381 }
382
383 argv += 2;
384 argc -= 2;
385 }
386
387 else if (argc > 3 && strcmp(argv[1], "--dissemble") == 0)
388 {
389 fp = fopen(argv[2], "rb");
390
391 if (fp == NULL)
392 {
393 perror(argv[2]);
394 fprintf(stderr, "timepng --dissemble %s: could not open for read\n",
395 argv[2]);
396 usage(NULL);
397 }
398
399 nfiles = atoi(argv[3]);
400 if (nfiles <= 0)
401 {
402 fprintf(stderr,
403 "timepng --dissemble <file> <count>: %s is not a count\n",
404 argv[3]);
405 exit(99);
406 }
407#ifdef __COVERITY__
408 else
409 {
410 nfiles &= PNG_UINT_31_MAX;
411 }
412#endif
413
414 argv += 3;
415 argc -= 3;
416 }
417
418 else /* Else use a temporary file */
419 {
420#ifndef __COVERITY__
421 fp = tmpfile();
422#else
423 /* Experimental. Coverity says tmpfile() is insecure because it
424 * generates predictable names.
425 *
426 * It is possible to satisfy Coverity by using mkstemp(); however,
427 * any platform supporting mkstemp() undoubtedly has a secure tmpfile()
428 * implementation as well, and doesn't need the fix. Note that
429 * the fix won't work on platforms that don't support mkstemp().
430 *
431 * https://www.securecoding.cert.org/confluence/display/c/
432 * FIO21-C.+Do+not+create+temporary+files+in+shared+directories
433 * says that most historic implementations of tmpfile() provide
434 * only a limited number of possible temporary file names
435 * (usually 26) before file names are recycled. That article also
436 * provides a secure solution that unfortunately depends upon mkstemp().
437 */
438 char tmpfile[] = "timepng-XXXXXX";
439 int filedes;
440 umask(0177);
441 filedes = mkstemp(tmpfile);
442 if (filedes < 0)
443 fp = NULL;
444 else
445 {
446 fp = fdopen(filedes,"w+");
447 /* Hide the filename immediately and ensure that the file does
448 * not exist after the program ends
449 */
450 (void) unlink(tmpfile);
451 }
452#endif
453
454 if (fp == NULL)
455 {
456 perror("tmpfile");
457 fprintf(stderr, "timepng: could not open the temporary file\n");
458 exit(1); /* not a user error */
459 }
460 }
461
462 /* Handle the transforms: */
463 while (argc > 1 && argv[1][0] == '-' && argv[1][1] == '-')
464 {
465 const char *opt = *++argv + 2;
466
467 --argc;
468
469 /* Transforms turn on the by-image processing and maybe set some
470 * transforms:
471 */
472 if (transforms == -1)
473 transforms = PNG_TRANSFORM_IDENTITY;
474
475 if (strcmp(opt, "by-image") == 0)
476 {
477 /* handled above */
478 }
479
480# define OPT(name) else if (strcmp(opt, #name) == 0)\
481 transforms |= PNG_TRANSFORM_ ## name
482
483 OPT(STRIP_16);
484 OPT(STRIP_ALPHA);
485 OPT(PACKING);
486 OPT(PACKSWAP);
487 OPT(EXPAND);
488 OPT(INVERT_MONO);
489 OPT(SHIFT);
490 OPT(BGR);
491 OPT(SWAP_ALPHA);
492 OPT(SWAP_ENDIAN);
493 OPT(INVERT_ALPHA);
494 OPT(STRIP_FILLER);
495 OPT(STRIP_FILLER_BEFORE);
496 OPT(STRIP_FILLER_AFTER);
497 OPT(GRAY_TO_RGB);
498 OPT(EXPAND_16);
499 OPT(SCALE_16);
500
501 else
502 {
503 fprintf(stderr, "timepng %s: unrecognized transform\n", opt);
504 usage(fp);
505 }
506 }
507
508 /* Handle the files: */
509 if (argc > 1 && nfiles > 0)
510 usage(fp); /* Additional files not valid with --dissemble */
511
512 else if (argc > 1)
513 {
514 int i;
515
516 for (i=1; i<argc; ++i)
517 {
518 if (nfiles == INT_MAX)
519 {
520 fprintf(stderr, "%s: skipped, too many files\n", argv[i]);
521 break;
522 }
523
524 else if (add_one_file(fp, argv[i]))
525 ++nfiles;
526 }
527 }
528
529 else if (nfiles == 0) /* Read from stdin without --dissemble */
530 {
531 char filename[FILENAME_MAX+1];
532
533 while (fgets(filename, FILENAME_MAX+1, stdin))
534 {
535 size_t len = strlen(filename);
536
537 if (filename[len-1] == '\n')
538 {
539 filename[len-1] = 0;
540 if (nfiles == INT_MAX)
541 {
542 fprintf(stderr, "%s: skipped, too many files\n", filename);
543 break;
544 }
545
546 else if (add_one_file(fp, filename))
547 ++nfiles;
548 }
549
550 else
551 {
552 fprintf(stderr, "timepng: file name too long: ...%s\n",
553 filename+len-32);
554 err = 1;
555 break;
556 }
557 }
558
559 if (ferror(stdin))
560 {
561 fprintf(stderr, "timepng: stdin: read error\n");
562 err = 1;
563 }
564 }
565
566 /* Perform the test, or produce the --assemble output: */
567 if (!err)
568 {
569 if (nfiles > 0)
570 {
571 if (assembly != NULL)
572 {
573 if (fflush(fp) && !ferror(fp) && fclose(fp))
574 {
575 perror(assembly);
576 fprintf(stderr, "%s: close failed\n", assembly);
577 }
578
579 else
580 {
581 printf("%s %d\n", assembly, nfiles);
582 fflush(stdout);
583 ok = !ferror(stdout);
584 }
585 }
586
587 else
588 {
589 ok = perform_one_test(fp, nfiles, transforms);
590 (void)fclose(fp);
591 }
592 }
593
594 else
595 usage(fp);
596 }
597
598 else
599 (void)fclose(fp);
600
601 /* Exit code 0 on success. */
602 return ok == 0;
603}
604#else /* !sufficient support */
605int main(void) { return 77; }
606#endif /* !sufficient support */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette