fukasawa e60969
/* timepng.c
fukasawa e60969
 *
fukasawa e60969
 * Copyright (c) 2013 John Cunningham Bowler
fukasawa e60969
 *
fukasawa e60969
 * Last changed in libpng 1.6.1 [March 28, 2013]
fukasawa e60969
 *
fukasawa e60969
 * This code is released under the libpng license.
fukasawa e60969
 * For conditions of distribution and use, see the disclaimer
fukasawa e60969
 * and license in png.h
fukasawa e60969
 *
fukasawa e60969
 * Load an arbitrary number of PNG files (from the command line, or, if there
fukasawa e60969
 * are no arguments on the command line, from stdin) then run a time test by
fukasawa e60969
 * reading each file by row.  The test does nothing with the read result and
fukasawa e60969
 * does no transforms.  The only output is a time as a floating point number of
fukasawa e60969
 * seconds with 9 decimal digits.
fukasawa e60969
 */
fukasawa e60969
#define _POSIX_C_SOURCE 199309L /* for clock_gettime */
fukasawa e60969
fukasawa e60969
#include <stdlib.h></stdlib.h>
fukasawa e60969
#include <stdio.h></stdio.h>
fukasawa e60969
#include <string.h></string.h>
fukasawa e60969
fukasawa e60969
#include <time.h></time.h>
fukasawa e60969
fukasawa e60969
#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
fukasawa e60969
#  include <config.h></config.h>
fukasawa e60969
#endif
fukasawa e60969
fukasawa e60969
/* Define the following to use this test against your installed libpng, rather
fukasawa e60969
 * than the one being built here:
fukasawa e60969
 */
fukasawa e60969
#ifdef PNG_FREESTANDING_TESTS
fukasawa e60969
#  include <png.h></png.h>
fukasawa e60969
#else
fukasawa e60969
#  include "../../png.h"
fukasawa e60969
#endif
fukasawa e60969
fukasawa e60969
static int read_png(FILE *fp)
fukasawa e60969
{
fukasawa e60969
   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
fukasawa e60969
   png_infop info_ptr = NULL;
fukasawa e60969
   png_bytep row = NULL, display = NULL;
fukasawa e60969
fukasawa e60969
   if (png_ptr == NULL)
fukasawa e60969
      return 0;
fukasawa e60969
fukasawa e60969
   if (setjmp(png_jmpbuf(png_ptr)))
fukasawa e60969
   {
fukasawa e60969
      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fukasawa e60969
      if (row != NULL) free(row);
fukasawa e60969
      if (display != NULL) free(display);
fukasawa e60969
      return 0;
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   png_init_io(png_ptr, fp);
fukasawa e60969
fukasawa e60969
   info_ptr = png_create_info_struct(png_ptr);
fukasawa e60969
   if (info_ptr == NULL)
fukasawa e60969
      png_error(png_ptr, "OOM allocating info structure");
fukasawa e60969
fukasawa e60969
   png_read_info(png_ptr, info_ptr);
fukasawa e60969
fukasawa e60969
   {
fukasawa e60969
      png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
fukasawa e60969
fukasawa e60969
      row = malloc(rowbytes);
fukasawa e60969
      display = malloc(rowbytes);
fukasawa e60969
fukasawa e60969
      if (row == NULL || display == NULL)
fukasawa e60969
         png_error(png_ptr, "OOM allocating row buffers");
fukasawa e60969
fukasawa e60969
      {
fukasawa e60969
         png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
fukasawa e60969
         int passes = png_set_interlace_handling(png_ptr);
fukasawa e60969
         int pass;
fukasawa e60969
fukasawa e60969
         png_start_read_image(png_ptr);
fukasawa e60969
fukasawa e60969
         for (pass = 0; pass < passes; ++pass)
fukasawa e60969
         {
fukasawa e60969
            png_uint_32 y = height;
fukasawa e60969
fukasawa e60969
            /* NOTE: this trashes the row each time; interlace handling won't
fukasawa e60969
             * work, but this avoids memory thrashing for speed testing.
fukasawa e60969
             */
fukasawa e60969
            while (y-- > 0)
fukasawa e60969
               png_read_row(png_ptr, row, display);
fukasawa e60969
         }
fukasawa e60969
      }
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   /* Make sure to read to the end of the file: */
fukasawa e60969
   png_read_end(png_ptr, info_ptr);
fukasawa e60969
   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fukasawa e60969
   free(row);
fukasawa e60969
   free(display);
fukasawa e60969
   return 1;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
static int mytime(struct timespec *t)
fukasawa e60969
{
fukasawa e60969
   /* Do the timing using clock_gettime and the per-process timer. */
fukasawa e60969
   if (!clock_gettime(CLOCK_PROCESS_CPUTIME_ID, t))
fukasawa e60969
      return 1;
fukasawa e60969
fukasawa e60969
   perror("CLOCK_PROCESS_CPUTIME_ID");
fukasawa e60969
   fprintf(stderr, "timepng: could not get the time\n");
fukasawa e60969
   return 0;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
static int perform_one_test(FILE *fp, int nfiles)
fukasawa e60969
{
fukasawa e60969
   int i;
fukasawa e60969
   struct timespec before, after;
fukasawa e60969
fukasawa e60969
   /* Clear out all errors: */
fukasawa e60969
   rewind(fp);
fukasawa e60969
fukasawa e60969
   if (mytime(&before))
fukasawa e60969
   {
fukasawa e60969
      for (i=0; i
fukasawa e60969
      {
fukasawa e60969
         if (read_png(fp))
fukasawa e60969
         {
fukasawa e60969
            if (ferror(fp))
fukasawa e60969
            {
fukasawa e60969
               perror("temporary file");
fukasawa e60969
               fprintf(stderr, "file %d: error reading PNG data\n", i);
fukasawa e60969
               return 0;
fukasawa e60969
            }
fukasawa e60969
         }
fukasawa e60969
fukasawa e60969
         else
fukasawa e60969
         {
fukasawa e60969
            perror("temporary file");
fukasawa e60969
            fprintf(stderr, "file %d: error from libpng\n", i);
fukasawa e60969
            return 0;
fukasawa e60969
         }
fukasawa e60969
      }
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   else
fukasawa e60969
      return 0;
fukasawa e60969
fukasawa e60969
   if (mytime(&after))
fukasawa e60969
   {
fukasawa e60969
      /* Work out the time difference and print it - this is the only output,
fukasawa e60969
       * so flush it immediately.
fukasawa e60969
       */
fukasawa e60969
      unsigned long s = after.tv_sec - before.tv_sec;
fukasawa e60969
      long ns = after.tv_nsec - before.tv_nsec;
fukasawa e60969
fukasawa e60969
      if (ns < 0)
fukasawa e60969
      {
fukasawa e60969
         --s;
fukasawa e60969
         ns += 1000000000;
fukasawa e60969
fukasawa e60969
         if (ns < 0)
fukasawa e60969
         {
fukasawa e60969
            fprintf(stderr, "timepng: bad clock from kernel\n");
fukasawa e60969
            return 0;
fukasawa e60969
         }
fukasawa e60969
      }
fukasawa e60969
fukasawa e60969
      printf("%lu.%.9ld\n", s, ns);
fukasawa e60969
      fflush(stdout);
fukasawa e60969
      if (ferror(stdout))
fukasawa e60969
      {
fukasawa e60969
         fprintf(stderr, "timepng: error writing output\n");
fukasawa e60969
         return 0;
fukasawa e60969
      }
fukasawa e60969
fukasawa e60969
      /* Successful return */
fukasawa e60969
      return 1;
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   else
fukasawa e60969
      return 0;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
static int add_one_file(FILE *fp, char *name)
fukasawa e60969
{
fukasawa e60969
   FILE *ip = fopen(name, "rb");
fukasawa e60969
fukasawa e60969
   if (ip != NULL)
fukasawa e60969
   {
fukasawa e60969
      int ch;
fukasawa e60969
      for (;;)
fukasawa e60969
      {
fukasawa e60969
         ch = getc(ip);
fukasawa e60969
         if (ch == EOF) break;
fukasawa e60969
         putc(ch, fp);
fukasawa e60969
      }
fukasawa e60969
fukasawa e60969
      if (ferror(ip))
fukasawa e60969
      {
fukasawa e60969
         perror(name);
fukasawa e60969
         fprintf(stderr, "%s: read error\n", name);
fukasawa e60969
         return 0;
fukasawa e60969
      }
fukasawa e60969
fukasawa e60969
      (void)fclose(ip);
fukasawa e60969
fukasawa e60969
      if (ferror(fp))
fukasawa e60969
      {
fukasawa e60969
         perror("temporary file");
fukasawa e60969
         fprintf(stderr, "temporary file write error\n");
fukasawa e60969
         return 0;
fukasawa e60969
      }
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   else
fukasawa e60969
   {
fukasawa e60969
      perror(name);
fukasawa e60969
      fprintf(stderr, "%s: open failed\n", name);
fukasawa e60969
      return 0;
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   return 1;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
int main(int argc, char **argv)
fukasawa e60969
{
fukasawa e60969
   int ok = 0;
fukasawa e60969
   FILE *fp = tmpfile();
fukasawa e60969
fukasawa e60969
   if (fp != NULL)
fukasawa e60969
   {
fukasawa e60969
      int err = 0;
fukasawa e60969
      int nfiles = 0;
fukasawa e60969
fukasawa e60969
      if (argc > 1)
fukasawa e60969
      {
fukasawa e60969
         int i;
fukasawa e60969
fukasawa e60969
         for (i=1; i
fukasawa e60969
         {
fukasawa e60969
            if (add_one_file(fp, argv[i]))
fukasawa e60969
               ++nfiles;
fukasawa e60969
fukasawa e60969
            else
fukasawa e60969
            {
fukasawa e60969
               err = 1;
fukasawa e60969
               break;
fukasawa e60969
            }
fukasawa e60969
         }
fukasawa e60969
      }
fukasawa e60969
fukasawa e60969
      else
fukasawa e60969
      {
fukasawa e60969
         char filename[FILENAME_MAX+1];
fukasawa e60969
fukasawa e60969
         while (fgets(filename, FILENAME_MAX+1, stdin))
fukasawa e60969
         {
fukasawa e60969
            size_t len = strlen(filename);
fukasawa e60969
fukasawa e60969
            if (filename[len-1] == '\n')
fukasawa e60969
            {
fukasawa e60969
               filename[len-1] = 0;
fukasawa e60969
               if (add_one_file(fp, filename))
fukasawa e60969
                  ++nfiles;
fukasawa e60969
fukasawa e60969
               else
fukasawa e60969
               {
fukasawa e60969
                  err = 1;
fukasawa e60969
                  break;
fukasawa e60969
               }
fukasawa e60969
            }
fukasawa e60969
fukasawa e60969
            else
fukasawa e60969
            {
fukasawa e60969
               fprintf(stderr, "timepng: truncated file name ...%s\n",
fukasawa e60969
                  filename+len-32);
fukasawa e60969
               err = 1;
fukasawa e60969
               break;
fukasawa e60969
            }
fukasawa e60969
         }
fukasawa e60969
fukasawa e60969
         if (ferror(stdin))
fukasawa e60969
         {
fukasawa e60969
            fprintf(stderr, "timepng: stdin: read error\n");
fukasawa e60969
            err = 1;
fukasawa e60969
         }
fukasawa e60969
      }
fukasawa e60969
fukasawa e60969
      if (!err)
fukasawa e60969
      {
fukasawa e60969
         if (nfiles > 0)
fukasawa e60969
            ok = perform_one_test(fp, nfiles);
fukasawa e60969
fukasawa e60969
         else
fukasawa e60969
            fprintf(stderr, "usage: timepng {files} or ls files | timepng\n");
fukasawa e60969
      }
fukasawa e60969
fukasawa e60969
      (void)fclose(fp);
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   else
fukasawa e60969
      fprintf(stderr, "timepng: could not open temporary file\n");
fukasawa e60969
fukasawa e60969
   /* Exit code 0 on success. */
fukasawa e60969
   return ok == 0;
fukasawa e60969
}