(root)/
libpng-1.6.40/
contrib/
examples/
simpleover.c
       1  /*- simpleover
       2   *
       3   * COPYRIGHT: Written by John Cunningham Bowler, 2015.
       4   * To the extent possible under law, the author has waived all copyright and
       5   * related or neighboring rights to this work.  This work is published from:
       6   * United States.
       7   *
       8   * Read several PNG files, which should have an alpha channel or transparency
       9   * information, and composite them together to produce one or more 16-bit linear
      10   * RGBA intermediates.  This involves doing the correct 'over' composition to
      11   * combine the alpha channels and corresponding data.
      12   *
      13   * Finally read an output (background) PNG using the 24-bit RGB format (the
      14   * PNG will be composited on green (#00ff00) by default if it has an alpha
      15   * channel), and apply the intermediate image generated above to specified
      16   * locations in the image.
      17   *
      18   * The command line has the general format:
      19   *
      20   *    simpleover <background.png> [output.png]
      21   *        {--sprite=width,height,name {[--at=x,y] {sprite.png}}}
      22   *        {--add=name {x,y}}
      23   *
      24   * The --sprite and --add options may occur multiple times. They are executed
      25   * in order.  --add may refer to any sprite already read.
      26   *
      27   * This code is intended to show how to composite multiple images together
      28   * correctly.  Apart from the libpng Simplified API the only work done in here
      29   * is to combine multiple input PNG images into a single sprite; this involves
      30   * a Porter-Duff 'over' operation and the input PNG images may, as a result,
      31   * be regarded as being layered one on top of the other with the first (leftmost
      32   * on the command line) being at the bottom and the last on the top.
      33   */
      34  #include <stddef.h>
      35  #include <stdlib.h>
      36  #include <string.h>
      37  #include <stdio.h>
      38  #include <errno.h>
      39  
      40  /* Normally use <png.h> here to get the installed libpng, but this is done to
      41   * ensure the code picks up the local libpng implementation, so long as this
      42   * file is linked against a sufficiently recent libpng (1.6+) it is ok to
      43   * change this to <png.h>:
      44   */
      45  #include "../../png.h"
      46  
      47  #ifdef PNG_SIMPLIFIED_READ_SUPPORTED
      48  
      49  #define sprite_name_chars 15
      50  struct sprite {
      51     FILE         *file;
      52     png_uint_16p  buffer;
      53     unsigned int  width;
      54     unsigned int  height;
      55     char          name[sprite_name_chars+1];
      56  };
      57  
      58  #if 0 /* div by 65535 test program */
      59  #include <math.h>
      60  #include <stdio.h>
      61  
      62  int main(void) {
      63     double err = 0;
      64     unsigned int xerr = 0;
      65     unsigned int r = 32769;
      66     {
      67        unsigned int x = 0;
      68  
      69        do {
      70           unsigned int t = x + (x >> 16) /*+ (x >> 31)*/ + r;
      71           double v = x, errtest;
      72  
      73           if (t < x) {
      74              fprintf(stderr, "overflow: %u+%u -> %u\n", x, r, t);
      75              return 1;
      76           }
      77  
      78           v /= 65535;
      79           errtest = v;
      80           t >>= 16;
      81           errtest -= t;
      82  
      83           if (errtest > err) {
      84              err = errtest;
      85              xerr = x;
      86  
      87              if (errtest >= .5) {
      88                 fprintf(stderr, "error: %u/65535 = %f, not %u, error %f\n",
      89                       x, v, t, errtest);
      90                 return 0;
      91              }
      92           }
      93        } while (++x <= 65535U*65535U);
      94     }
      95  
      96     printf("error %f @ %u\n", err, xerr);
      97  
      98     return 0;
      99  }
     100  #endif /* div by 65535 test program */
     101  
     102  static void
     103  sprite_op(const struct sprite *sprite, int x_offset, int y_offset,
     104     png_imagep image, const png_uint_16 *buffer)
     105  {
     106     /* This is where the Porter-Duff 'Over' operator is evaluated; change this
     107      * code to change the operator (this could be parameterized).  Any other
     108      * image processing operation could be used here.
     109      */
     110  
     111  
     112     /* Check for an x or y offset that pushes any part of the image beyond the
     113      * right or bottom of the sprite:
     114      */
     115     if ((y_offset < 0 || (unsigned)/*SAFE*/y_offset < sprite->height) &&
     116         (x_offset < 0 || (unsigned)/*SAFE*/x_offset < sprite->width))
     117     {
     118        unsigned int y = 0;
     119  
     120        if (y_offset < 0)
     121           y = -y_offset; /* Skip to first visible row */
     122  
     123        do
     124        {
     125           unsigned int x = 0;
     126  
     127           if (x_offset < 0)
     128              x = -x_offset;
     129  
     130           do
     131           {
     132              /* In and out are RGBA values, so: */
     133              const png_uint_16 *in_pixel = buffer + (y * image->width + x)*4;
     134              png_uint_32 in_alpha = in_pixel[3];
     135  
     136              /* This is the optimized Porter-Duff 'Over' operation, when the
     137               * input alpha is 0 the output is not changed.
     138               */
     139              if (in_alpha > 0)
     140              {
     141                 png_uint_16 *out_pixel = sprite->buffer +
     142                    ((y+y_offset) * sprite->width + (x+x_offset))*4;
     143  
     144                 /* This is the weight to apply to the output: */
     145                 in_alpha = 65535-in_alpha;
     146  
     147                 if (in_alpha > 0)
     148                 {
     149                    /* The input must be composed onto the output. This means
     150                     * multiplying the current output pixel value by the inverse
     151                     * of the input alpha (1-alpha). A division is required but
     152                     * it is by the constant 65535.  Approximate this as:
     153                     *
     154                     *     (x + (x >> 16) + 32769) >> 16;
     155                     *
     156                     * This is exact (and does not overflow) for all values of
     157                     * x in the range 0..65535*65535.  (Note that the calculation
     158                     * produces the closest integer; the maximum error is <0.5).
     159                     */
     160                    png_uint_32 tmp;
     161  
     162  #                 define compose(c)\
     163                       tmp = out_pixel[c] * in_alpha;\
     164                       tmp = (tmp + (tmp >> 16) + 32769) >> 16;\
     165                       out_pixel[c] = tmp + in_pixel[c]
     166  
     167                    /* The following is very vectorizable... */
     168                    compose(0);
     169                    compose(1);
     170                    compose(2);
     171                    compose(3);
     172                 }
     173  
     174                 else
     175                    out_pixel[0] = in_pixel[0],
     176                    out_pixel[1] = in_pixel[1],
     177                    out_pixel[2] = in_pixel[2],
     178                    out_pixel[3] = in_pixel[3];
     179              }
     180           }
     181           while (++x < image->width);
     182        }
     183        while (++y < image->height);
     184     }
     185  }
     186  
     187  static int
     188  create_sprite(struct sprite *sprite, int *argc, const char ***argv)
     189  {
     190     /* Read the arguments and create this sprite. The sprite buffer has already
     191      * been allocated. This reads the input PNGs one by one in linear format,
     192      * composes them onto the sprite buffer (the code in the function above)
     193      * then saves the result, converting it on the fly to PNG RGBA 8-bit format.
     194      */
     195     while (*argc > 0)
     196     {
     197        char tombstone;
     198        int x = 0, y = 0;
     199  
     200        if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-')
     201        {
     202           /* The only supported option is --at. */
     203           if (sscanf((*argv)[0], "--at=%d,%d%c", &x, &y, &tombstone) != 2)
     204              break; /* success; caller will parse this option */
     205  
     206           ++*argv, --*argc;
     207        }
     208  
     209        else
     210        {
     211           /* The argument has to be a file name */
     212           png_image image;
     213  
     214           image.version = PNG_IMAGE_VERSION;
     215           image.opaque = NULL;
     216  
     217           if (png_image_begin_read_from_file(&image, (*argv)[0]))
     218           {
     219              png_uint_16p buffer;
     220  
     221              image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
     222  
     223              buffer = malloc(PNG_IMAGE_SIZE(image));
     224  
     225              if (buffer != NULL)
     226              {
     227                 if (png_image_finish_read(&image, NULL/*background*/, buffer,
     228                    0/*row_stride*/,
     229                    NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/))
     230                 {
     231                    /* This is the place where the Porter-Duff 'Over' operator
     232                     * needs to be done by this code.  In fact, any image
     233                     * processing required can be done here; the data is in
     234                     * the correct format (linear, 16-bit) and source and
     235                     * destination are in memory.
     236                     */
     237                    sprite_op(sprite, x, y, &image, buffer);
     238                    free(buffer);
     239                    ++*argv, --*argc;
     240                    /* And continue to the next argument */
     241                    continue;
     242                 }
     243  
     244                 else
     245                 {
     246                    free(buffer);
     247                    fprintf(stderr, "simpleover: read %s: %s\n", (*argv)[0],
     248                        image.message);
     249                 }
     250              }
     251  
     252              else
     253              {
     254                 fprintf(stderr, "simpleover: out of memory: %lu bytes\n",
     255                    (unsigned long)PNG_IMAGE_SIZE(image));
     256  
     257                 /* png_image_free must be called if we abort the Simplified API
     258                  * read because of a problem detected in this code.  If problems
     259                  * are detected in the Simplified API it cleans up itself.
     260                  */
     261                 png_image_free(&image);
     262              }
     263           }
     264  
     265           else
     266           {
     267              /* Failed to read the first argument: */
     268              fprintf(stderr, "simpleover: %s: %s\n", (*argv)[0], image.message);
     269           }
     270  
     271           return 0; /* failure */
     272        }
     273     }
     274  
     275     /* All the sprite operations have completed successfully. Save the RGBA
     276      * buffer as a PNG using the simplified write API.
     277      */
     278     sprite->file = tmpfile();
     279  
     280     if (sprite->file != NULL)
     281     {
     282        png_image save;
     283  
     284        memset(&save, 0, sizeof save);
     285        save.version = PNG_IMAGE_VERSION;
     286        save.opaque = NULL;
     287        save.width = sprite->width;
     288        save.height = sprite->height;
     289        save.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
     290        save.flags = PNG_IMAGE_FLAG_FAST;
     291        save.colormap_entries = 0;
     292  
     293        if (png_image_write_to_stdio(&save, sprite->file, 1/*convert_to_8_bit*/,
     294            sprite->buffer, 0/*row_stride*/, NULL/*colormap*/))
     295        {
     296           /* Success; the buffer is no longer needed: */
     297           free(sprite->buffer);
     298           sprite->buffer = NULL;
     299           return 1; /* ok */
     300        }
     301  
     302        else
     303           fprintf(stderr, "simpleover: write sprite %s: %s\n", sprite->name,
     304              save.message);
     305     }
     306  
     307     else
     308        fprintf(stderr, "simpleover: sprite %s: could not allocate tmpfile: %s\n",
     309           sprite->name, strerror(errno));
     310  
     311     return 0; /* fail */
     312  }
     313  
     314  static int
     315  add_sprite(png_imagep output, png_bytep out_buf, struct sprite *sprite,
     316     int *argc, const char ***argv)
     317  {
     318     /* Given a --add argument naming this sprite, perform the operations listed
     319      * in the following arguments.  The arguments are expected to have the form
     320      * (x,y), which is just an offset at which to add the sprite to the
     321      * output.
     322      */
     323     while (*argc > 0)
     324     {
     325        char tombstone;
     326        int x, y;
     327  
     328        if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-')
     329           return 1; /* success */
     330  
     331        if (sscanf((*argv)[0], "%d,%d%c", &x, &y, &tombstone) == 2)
     332        {
     333           /* Now add the new image into the sprite data, but only if it
     334            * will fit.
     335            */
     336           if (x < 0 || y < 0 ||
     337               (unsigned)/*SAFE*/x >= output->width ||
     338               (unsigned)/*SAFE*/y >= output->height ||
     339               sprite->width > output->width-x ||
     340               sprite->height > output->height-y)
     341           {
     342              fprintf(stderr, "simpleover: sprite %s @ (%d,%d) outside image\n",
     343                 sprite->name, x, y);
     344              /* Could just skip this, but for the moment it is an error */
     345              return 0; /* error */
     346           }
     347  
     348           else
     349           {
     350              /* Since we know the sprite fits we can just read it into the
     351               * output using the simplified API.
     352               */
     353              png_image in;
     354  
     355              in.version = PNG_IMAGE_VERSION;
     356              rewind(sprite->file);
     357  
     358              if (png_image_begin_read_from_stdio(&in, sprite->file))
     359              {
     360                 in.format = PNG_FORMAT_RGB; /* force compose */
     361  
     362                 if (png_image_finish_read(&in, NULL/*background*/,
     363                    out_buf + (y*output->width + x)*3/*RGB*/,
     364                    output->width*3/*row_stride*/,
     365                    NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/))
     366                 {
     367                    ++*argv, --*argc;
     368                    continue;
     369                 }
     370              }
     371  
     372              /* The read failed: */
     373              fprintf(stderr, "simpleover: add sprite %s: %s\n", sprite->name,
     374                  in.message);
     375              return 0; /* error */
     376           }
     377        }
     378  
     379        else
     380        {
     381           fprintf(stderr, "simpleover: --add='%s': invalid position %s\n",
     382                 sprite->name, (*argv)[0]);
     383           return 0; /* error */
     384        }
     385     }
     386  
     387     return 1; /* ok */
     388  }
     389  
     390  static int
     391  simpleover_process(png_imagep output, png_bytep out_buf, int argc,
     392     const char **argv)
     393  {
     394     int result = 1; /* success */
     395  #  define csprites 10/*limit*/
     396  #  define str(a) #a
     397     int nsprites = 0;
     398     struct sprite sprites[csprites];
     399  
     400     while (argc > 0)
     401     {
     402        result = 0; /* fail */
     403  
     404        if (strncmp(argv[0], "--sprite=", 9) == 0)
     405        {
     406           char tombstone;
     407  
     408           if (nsprites < csprites)
     409           {
     410              int n;
     411  
     412              sprites[nsprites].width = sprites[nsprites].height = 0;
     413              sprites[nsprites].name[0] = 0;
     414  
     415              n = sscanf(argv[0], "--sprite=%u,%u,%" str(sprite_name_chars) "s%c",
     416                  &sprites[nsprites].width, &sprites[nsprites].height,
     417                  sprites[nsprites].name, &tombstone);
     418  
     419              if ((n == 2 || n == 3) &&
     420                  sprites[nsprites].width > 0 && sprites[nsprites].height > 0)
     421              {
     422                 size_t buf_size, tmp;
     423  
     424                 /* Default a name if not given. */
     425                 if (sprites[nsprites].name[0] == 0)
     426                    sprintf(sprites[nsprites].name, "sprite-%d", nsprites+1);
     427  
     428                 /* Allocate a buffer for the sprite and calculate the buffer
     429                  * size:
     430                  */
     431                 buf_size = sizeof (png_uint_16 [4]);
     432                 buf_size *= sprites[nsprites].width;
     433                 buf_size *= sprites[nsprites].height;
     434  
     435                 /* This can overflow a (size_t); check for this: */
     436                 tmp = buf_size;
     437                 tmp /= sprites[nsprites].width;
     438                 tmp /= sprites[nsprites].height;
     439  
     440                 if (tmp == sizeof (png_uint_16 [4]))
     441                 {
     442                    sprites[nsprites].buffer = malloc(buf_size);
     443                    /* This buffer must be initialized to transparent: */
     444                    memset(sprites[nsprites].buffer, 0, buf_size);
     445  
     446                    if (sprites[nsprites].buffer != NULL)
     447                    {
     448                       sprites[nsprites].file = NULL;
     449                       ++argv, --argc;
     450  
     451                       if (create_sprite(sprites+nsprites++, &argc, &argv))
     452                       {
     453                          result = 1; /* still ok */
     454                          continue;
     455                       }
     456  
     457                       break; /* error */
     458                    }
     459                 }
     460  
     461                 /* Overflow, or OOM */
     462                 fprintf(stderr, "simpleover: %s: sprite too large\n", argv[0]);
     463                 break;
     464              }
     465  
     466              else
     467              {
     468                 fprintf(stderr, "simpleover: %s: invalid sprite (%u,%u)\n",
     469                    argv[0], sprites[nsprites].width, sprites[nsprites].height);
     470                 break;
     471              }
     472           }
     473  
     474           else
     475           {
     476              fprintf(stderr, "simpleover: %s: too many sprites\n", argv[0]);
     477              break;
     478           }
     479        }
     480  
     481        else if (strncmp(argv[0], "--add=", 6) == 0)
     482        {
     483           const char *name = argv[0]+6;
     484           int isprite = nsprites;
     485  
     486           ++argv, --argc;
     487  
     488           while (--isprite >= 0)
     489           {
     490              if (strcmp(sprites[isprite].name, name) == 0)
     491              {
     492                 if (!add_sprite(output, out_buf, sprites+isprite, &argc, &argv))
     493                    goto out; /* error in add_sprite */
     494  
     495                 break;
     496              }
     497           }
     498  
     499           if (isprite < 0) /* sprite not found */
     500           {
     501              fprintf(stderr, "simpleover: --add='%s': sprite not found\n", name);
     502              break;
     503           }
     504        }
     505  
     506        else
     507        {
     508           fprintf(stderr, "simpleover: %s: unrecognized operation\n", argv[0]);
     509           break;
     510        }
     511  
     512        result = 1; /* ok  */
     513     }
     514  
     515     /* Clean up the cache of sprites: */
     516  out:
     517     while (--nsprites >= 0)
     518     {
     519        if (sprites[nsprites].buffer != NULL)
     520           free(sprites[nsprites].buffer);
     521  
     522        if (sprites[nsprites].file != NULL)
     523           (void)fclose(sprites[nsprites].file);
     524     }
     525  
     526     return result;
     527  }
     528  
     529  int main(int argc, const char **argv)
     530  {
     531     int result = 1; /* default to fail */
     532  
     533     if (argc >= 2)
     534     {
     535        int argi = 2;
     536        const char *output = NULL;
     537        png_image image;
     538  
     539        if (argc > 2 && argv[2][0] != '-'/*an operation*/)
     540        {
     541           output = argv[2];
     542           argi = 3;
     543        }
     544  
     545        image.version = PNG_IMAGE_VERSION;
     546        image.opaque = NULL;
     547  
     548        if (png_image_begin_read_from_file(&image, argv[1]))
     549        {
     550           png_bytep buffer;
     551  
     552           image.format = PNG_FORMAT_RGB; /* 24-bit RGB */
     553  
     554           buffer = malloc(PNG_IMAGE_SIZE(image));
     555  
     556           if (buffer != NULL)
     557           {
     558              png_color background = {0, 0xff, 0}; /* fully saturated green */
     559  
     560              if (png_image_finish_read(&image, &background, buffer,
     561                 0/*row_stride*/, NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP */))
     562              {
     563                 /* At this point png_image_finish_read has cleaned up the
     564                  * allocated data in png_image, and only the buffer needs to be
     565                  * freed.
     566                  *
     567                  * Perform the remaining operations:
     568                  */
     569                 if (simpleover_process(&image, buffer, argc-argi, argv+argi))
     570                 {
     571                    /* Write the output: */
     572                    if ((output != NULL &&
     573                         png_image_write_to_file(&image, output,
     574                          0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
     575                          NULL/*colormap*/)) ||
     576                        (output == NULL &&
     577                         png_image_write_to_stdio(&image, stdout,
     578                          0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
     579                          NULL/*colormap*/)))
     580                       result = 0;
     581  
     582                    else
     583                       fprintf(stderr, "simpleover: write %s: %s\n",
     584                          output == NULL ? "stdout" : output, image.message);
     585                 }
     586  
     587                 /* else simpleover_process writes an error message */
     588              }
     589  
     590              else
     591                 fprintf(stderr, "simpleover: read %s: %s\n", argv[1],
     592                     image.message);
     593  
     594              free(buffer);
     595           }
     596  
     597           else
     598           {
     599              fprintf(stderr, "simpleover: out of memory: %lu bytes\n",
     600                 (unsigned long)PNG_IMAGE_SIZE(image));
     601  
     602              /* This is the only place where a 'free' is required; libpng does
     603               * the cleanup on error and success, but in this case we couldn't
     604               * complete the read because of running out of memory.
     605               */
     606              png_image_free(&image);
     607           }
     608        }
     609  
     610        else
     611        {
     612           /* Failed to read the first argument: */
     613           fprintf(stderr, "simpleover: %s: %s\n", argv[1], image.message);
     614        }
     615     }
     616  
     617     else
     618     {
     619        /* Usage message */
     620        fprintf(stderr,
     621           "simpleover: usage: simpleover background.png [output.png]\n"
     622           "  Output 'background.png' as a 24-bit RGB PNG file in 'output.png'\n"
     623           "   or, if not given, stdout.  'background.png' will be composited\n"
     624           "   on fully saturated green.\n"
     625           "\n"
     626           "  Optionally, before output, process additional PNG files:\n"
     627           "\n"
     628           "   --sprite=width,height,name {[--at=x,y] {sprite.png}}\n"
     629           "    Produce a transparent sprite of size (width,height) and with\n"
     630           "     name 'name'.\n"
     631           "    For each sprite.png composite it using a Porter-Duff 'Over'\n"
     632           "     operation at offset (x,y) in the sprite (defaulting to (0,0)).\n"
     633           "     Input PNGs will be truncated to the area of the sprite.\n"
     634           "\n"
     635           "   --add='name' {x,y}\n"
     636           "    Optionally, before output, composite a sprite, 'name', which\n"
     637           "     must have been previously produced using --sprite, at each\n"
     638           "     offset (x,y) in the output image.  Each sprite must fit\n"
     639           "     completely within the output image.\n"
     640           "\n"
     641           "  PNG files are processed in the order they occur on the command\n"
     642           "  line and thus the first PNG processed appears as the bottommost\n"
     643           "  in the output image.\n");
     644     }
     645  
     646     return result;
     647  }
     648  #endif /* SIMPLIFIED_READ */