1/* timepng.c 2 * 3 * Copyright (c) 2013 John Cunningham Bowler 4 * 5 * Last changed in libpng 1.6.1 [March 28, 2013] 6 * 7 * This code is released under the libpng license. 8 * For conditions of distribution and use, see the disclaimer 9 * and license in png.h 10 * 11 * Load an arbitrary number of PNG files (from the command line, or, if there 12 * are no arguments on the command line, from stdin) then run a time test by 13 * reading each file by row. The test does nothing with the read result and 14 * does no transforms. The only output is a time as a floating point number of 15 * seconds with 9 decimal digits. 16 */ 17#define _POSIX_C_SOURCE 199309L /* for clock_gettime */ 18 19#include <stdlib.h> 20#include <stdio.h> 21#include <string.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 38static int read_png(FILE *fp) 39{ 40 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); 41 png_infop info_ptr = NULL; 42 png_bytep row = NULL, display = NULL; 43 44 if (png_ptr == NULL) 45 return 0; 46 47 if (setjmp(png_jmpbuf(png_ptr))) 48 { 49 png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 50 if (row != NULL) free(row); 51 if (display != NULL) free(display); 52 return 0; 53 } 54 55 png_init_io(png_ptr, fp); 56 57 info_ptr = png_create_info_struct(png_ptr); 58 if (info_ptr == NULL) 59 png_error(png_ptr, "OOM allocating info structure"); 60 61 png_read_info(png_ptr, info_ptr); 62 63 { 64 png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); 65 66 row = malloc(rowbytes); 67 display = malloc(rowbytes); 68 69 if (row == NULL || display == NULL) 70 png_error(png_ptr, "OOM allocating row buffers"); 71 72 { 73 png_uint_32 height = png_get_image_height(png_ptr, info_ptr); 74 int passes = png_set_interlace_handling(png_ptr); 75 int pass; 76 77 png_start_read_image(png_ptr); 78 79 for (pass = 0; pass < passes; ++pass) 80 { 81 png_uint_32 y = height; 82 83 /* NOTE: this trashes the row each time; interlace handling won't 84 * work, but this avoids memory thrashing for speed testing. 85 */ 86 while (y-- > 0) 87 png_read_row(png_ptr, row, display); 88 } 89 } 90 } 91 92 /* Make sure to read to the end of the file: */ 93 png_read_end(png_ptr, info_ptr); 94 png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 95 free(row); 96 free(display); 97 return 1; 98} 99 100static int mytime(struct timespec *t) 101{ 102 /* Do the timing using clock_gettime and the per-process timer. */ 103 if (!clock_gettime(CLOCK_PROCESS_CPUTIME_ID, t)) 104 return 1; 105 106 perror("CLOCK_PROCESS_CPUTIME_ID"); 107 fprintf(stderr, "timepng: could not get the time\n"); 108 return 0; 109} 110 111static int perform_one_test(FILE *fp, int nfiles) 112{ 113 int i; 114 struct timespec before, after; 115 116 /* Clear out all errors: */ 117 rewind(fp); 118 119 if (mytime(&before)) 120 { 121 for (i=0; i<nfiles; ++i) 122 { 123 if (read_png(fp)) 124 { 125 if (ferror(fp)) 126 { 127 perror("temporary file"); 128 fprintf(stderr, "file %d: error reading PNG data\n", i); 129 return 0; 130 } 131 } 132 133 else 134 { 135 perror("temporary file"); 136 fprintf(stderr, "file %d: error from libpng\n", i); 137 return 0; 138 } 139 } 140 } 141 142 else 143 return 0; 144 145 if (mytime(&after)) 146 { 147 /* Work out the time difference and print it - this is the only output, 148 * so flush it immediately. 149 */ 150 unsigned long s = after.tv_sec - before.tv_sec; 151 long ns = after.tv_nsec - before.tv_nsec; 152 153 if (ns < 0) 154 { 155 --s; 156 ns += 1000000000; 157 158 if (ns < 0) 159 { 160 fprintf(stderr, "timepng: bad clock from kernel\n"); 161 return 0; 162 } 163 } 164 165 printf("%lu.%.9ld\n", s, ns); 166 fflush(stdout); 167 if (ferror(stdout)) 168 { 169 fprintf(stderr, "timepng: error writing output\n"); 170 return 0; 171 } 172 173 /* Successful return */ 174 return 1; 175 } 176 177 else 178 return 0; 179} 180 181static int add_one_file(FILE *fp, char *name) 182{ 183 FILE *ip = fopen(name, "rb"); 184 185 if (ip != NULL) 186 { 187 int ch; 188 for (;;) 189 { 190 ch = getc(ip); 191 if (ch == EOF) break; 192 putc(ch, fp); 193 } 194 195 if (ferror(ip)) 196 { 197 perror(name); 198 fprintf(stderr, "%s: read error\n", name); 199 return 0; 200 } 201 202 (void)fclose(ip); 203 204 if (ferror(fp)) 205 { 206 perror("temporary file"); 207 fprintf(stderr, "temporary file write error\n"); 208 return 0; 209 } 210 } 211 212 else 213 { 214 perror(name); 215 fprintf(stderr, "%s: open failed\n", name); 216 return 0; 217 } 218 219 return 1; 220} 221 222int main(int argc, char **argv) 223{ 224 int ok = 0; 225 FILE *fp = tmpfile(); 226 227 if (fp != NULL) 228 { 229 int err = 0; 230 int nfiles = 0; 231 232 if (argc > 1) 233 { 234 int i; 235 236 for (i=1; i<argc; ++i) 237 { 238 if (add_one_file(fp, argv[i])) 239 ++nfiles; 240 241 else 242 { 243 err = 1; 244 break; 245 } 246 } 247 } 248 249 else 250 { 251 char filename[FILENAME_MAX+1]; 252 253 while (fgets(filename, FILENAME_MAX+1, stdin)) 254 { 255 size_t len = strlen(filename); 256 257 if (filename[len-1] == '\n') 258 { 259 filename[len-1] = 0; 260 if (add_one_file(fp, filename)) 261 ++nfiles; 262 263 else 264 { 265 err = 1; 266 break; 267 } 268 } 269 270 else 271 { 272 fprintf(stderr, "timepng: truncated file name ...%s\n", 273 filename+len-32); 274 err = 1; 275 break; 276 } 277 } 278 279 if (ferror(stdin)) 280 { 281 fprintf(stderr, "timepng: stdin: read error\n"); 282 err = 1; 283 } 284 } 285 286 if (!err) 287 { 288 if (nfiles > 0) 289 ok = perform_one_test(fp, nfiles); 290 291 else 292 fprintf(stderr, "usage: timepng {files} or ls files | timepng\n"); 293 } 294 295 (void)fclose(fp); 296 } 297 298 else 299 fprintf(stderr, "timepng: could not open temporary file\n"); 300 301 /* Exit code 0 on success. */ 302 return ok == 0; 303} 304