19b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/*- simpleover
29b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
39b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * COPYRIGHT: Written by John Cunningham Bowler, 2015.
49b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * To the extent possible under law, the author has waived all copyright and
59b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * related or neighboring rights to this work.  This work is published from:
69b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * United States.
79b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
89b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * Read several PNG files, which should have an alpha channel or transparency
99b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * information, and composite them together to produce one or more 16-bit linear
109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * RGBA intermediates.  This involves doing the correct 'over' composition to
119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * combine the alpha channels and corresponding data.
129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * Finally read an output (background) PNG using the 24-bit RGB format (the
149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * PNG will be composited on green (#00ff00) by default if it has an alpha
159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * channel), and apply the intermediate image generated above to specified
169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * locations in the image.
179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * The command line has the general format:
199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    simpleover <background.png> [output.png]
219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *        {--sprite=width,height,name {[--at=x,y] {sprite.png}}}
229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *        {--add=name {x,y}}
239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * The --sprite and --add options may occur multiple times. They are executed
259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * in order.  --add may refer to any sprite already read.
269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * This code is intended to show how to composite multiple images together
289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * correctly.  Apart from the libpng Simplified API the only work done in here
299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * is to combine multiple input PNG images into a single sprite; this involves
309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * a Porter-Duff 'over' operation and the input PNG images may, as a result,
319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * be regarded as being layered one on top of the other with the first (leftmost
329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * on the command line) being at the bottom and the last on the top.
339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett */
349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <stddef.h>
359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <stdlib.h>
369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <string.h>
379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <stdio.h>
389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <errno.h>
399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/* Normally use <png.h> here to get the installed libpng, but this is done to
419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * ensure the code picks up the local libpng implementation, so long as this
429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * file is linked against a sufficiently recent libpng (1.6+) it is ok to
439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * change this to <png.h>:
449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett */
459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include "../../png.h"
469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define sprite_name_chars 15
509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstruct sprite {
519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   FILE         *file;
529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   png_uint_16p  buffer;
539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   unsigned int  width;
549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   unsigned int  height;
559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   char          name[sprite_name_chars+1];
569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett};
579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#if 0 /* div by 65535 test program */
599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <math.h>
609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <stdio.h>
619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettint main(void) {
639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double err = 0;
649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   unsigned int xerr = 0;
659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   unsigned int r = 32769;
669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      unsigned int x = 0;
689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      do {
709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         unsigned int t = x + (x >> 16) /*+ (x >> 31)*/ + r;
719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         double v = x, errtest;
729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (t < x) {
749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            fprintf(stderr, "overflow: %u+%u -> %u\n", x, r, t);
759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            return 1;
769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         v /= 65535;
799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         errtest = v;
809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         t >>= 16;
819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         errtest -= t;
829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (errtest > err) {
849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            err = errtest;
859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            xerr = x;
869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            if (errtest >= .5) {
889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               fprintf(stderr, "error: %u/65535 = %f, not %u, error %f\n",
899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     x, v, t, errtest);
909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               return 0;
919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            }
929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      } while (++x <= 65535U*65535U);
949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   printf("error %f @ %u\n", err, xerr);
979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return 0;
999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
1009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#endif /* div by 65535 test program */
1019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic void
1039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettsprite_op(const struct sprite *sprite, int x_offset, int y_offset,
1049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   png_imagep image, const png_uint_16 *buffer)
1059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
1069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* This is where the Porter-Duff 'Over' operator is evaluated; change this
1079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * code to change the operator (this could be parameterized).  Any other
1089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * image processing operation could be used here.
1099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
1109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Check for an x or y offset that pushes any part of the image beyond the
1139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * right or bottom of the sprite:
1149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
1159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if ((y_offset < 0 || (unsigned)/*SAFE*/y_offset < sprite->height) &&
1169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       (x_offset < 0 || (unsigned)/*SAFE*/x_offset < sprite->width))
1179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
1189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      unsigned int y = 0;
1199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (y_offset < 0)
1219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         y = -y_offset; /* Skip to first visible row */
1229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      do
1249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
1259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         unsigned int x = 0;
1269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (x_offset < 0)
1289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            x = -x_offset;
1299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         do
1319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
1329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            /* In and out are RGBA values, so: */
1339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            const png_uint_16 *in_pixel = buffer + (y * image->width + x)*4;
1349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            png_uint_32 in_alpha = in_pixel[3];
1359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            /* This is the optimized Porter-Duff 'Over' operation, when the
1379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             * input alpha is 0 the output is not changed.
1389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             */
13906f1087a94f1e48298e89d77ccc51a0ced871958Matt Sarett            if (in_alpha > 0)
1409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            {
1419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               png_uint_16 *out_pixel = sprite->buffer +
1429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  ((y+y_offset) * sprite->width + (x+x_offset))*4;
1439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               /* This is the weight to apply to the output: */
1459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               in_alpha = 65535-in_alpha;
1469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               if (in_alpha > 0)
1489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               {
1499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  /* The input must be composed onto the output. This means
1509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   * multiplying the current output pixel value by the inverse
1519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   * of the input alpha (1-alpha). A division is required but
1529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   * it is by the constant 65535.  Approximate this as:
1539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   *
1549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   *     (x + (x >> 16) + 32769) >> 16;
1559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   *
1569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   * This is exact (and does not overflow) for all values of
1579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   * x in the range 0..65535*65535.  (Note that the calculation
1589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   * produces the closest integer; the maximum error is <0.5).
1599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   */
1609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  png_uint_32 tmp;
1619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#                 define compose(c)\
1639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     tmp = out_pixel[c] * in_alpha;\
1649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     tmp = (tmp + (tmp >> 16) + 32769) >> 16;\
1659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     out_pixel[c] = tmp + in_pixel[c]
1669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  /* The following is very vectorizable... */
1689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  compose(0);
1699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  compose(1);
1709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  compose(2);
1719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  compose(3);
1729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               }
1739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               else
1759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  out_pixel[0] = in_pixel[0],
1769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  out_pixel[1] = in_pixel[1],
1779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  out_pixel[2] = in_pixel[2],
1789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  out_pixel[3] = in_pixel[3];
1799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            }
1809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
1819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         while (++x < image->width);
1829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
1839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      while (++y < image->height);
1849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
1859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
1869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
18706f1087a94f1e48298e89d77ccc51a0ced871958Matt Sarettstatic int
1889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettcreate_sprite(struct sprite *sprite, int *argc, const char ***argv)
1899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
1909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Read the arguments and create this sprite. The sprite buffer has already
1919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * been allocated. This reads the input PNGs one by one in linear format,
1929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * composes them onto the sprite buffer (the code in the function above)
1939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * then saves the result, converting it on the fly to PNG RGBA 8-bit format.
1949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
1959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   while (*argc > 0)
1969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
1979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      char tombstone;
1989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      int x = 0, y = 0;
1999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-')
2019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
2029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         /* The only supported option is --at. */
2039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (sscanf((*argv)[0], "--at=%d,%d%c", &x, &y, &tombstone) != 2)
2049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            break; /* success; caller will parse this option */
2059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         ++*argv, --*argc;
2079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
2089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      else
2109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
2119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         /* The argument has to be a file name */
2129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         png_image image;
2139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         image.version = PNG_IMAGE_VERSION;
2159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         image.opaque = NULL;
2169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (png_image_begin_read_from_file(&image, (*argv)[0]))
2189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
2199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            png_uint_16p buffer;
2209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
2229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            buffer = malloc(PNG_IMAGE_SIZE(image));
2249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            if (buffer != NULL)
2269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            {
2279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               if (png_image_finish_read(&image, NULL/*background*/, buffer,
2289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  0/*row_stride*/,
2299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/))
2309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               {
2319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  /* This is the place where the Porter-Duff 'Over' operator
2329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   * needs to be done by this code.  In fact, any image
2339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   * processing required can be done here; the data is in
2349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   * the correct format (linear, 16-bit) and source and
2359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   * destination are in memory.
2369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   */
2379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  sprite_op(sprite, x, y, &image, buffer);
2389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  free(buffer);
2399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  ++*argv, --*argc;
2409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  /* And continue to the next argument */
2419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  continue;
2429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               }
2439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               else
2459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               {
2469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  free(buffer);
2479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  fprintf(stderr, "simpleover: read %s: %s\n", (*argv)[0],
2489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                      image.message);
2499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               }
2509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            }
2519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            else
2539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            {
2549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               fprintf(stderr, "simpleover: out of memory: %lu bytes\n",
2559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  (unsigned long)PNG_IMAGE_SIZE(image));
2569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               /* png_image_free must be called if we abort the Simplified API
2589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                * read because of a problem detected in this code.  If problems
2599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                * are detected in the Simplified API it cleans up itself.
2609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                */
2619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               png_image_free(&image);
2629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            }
2639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
2649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         else
2669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
2679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            /* Failed to read the first argument: */
2689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            fprintf(stderr, "simpleover: %s: %s\n", (*argv)[0], image.message);
2699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
2709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return 0; /* failure */
2729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
2739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
2749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* All the sprite operations have completed successfully. Save the RGBA
2769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * buffer as a PNG using the simplified write API.
2779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
2789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   sprite->file = tmpfile();
2799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (sprite->file != NULL)
2819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
2829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      png_image save;
2839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      memset(&save, 0, sizeof save);
2859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      save.version = PNG_IMAGE_VERSION;
2869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      save.opaque = NULL;
2879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      save.width = sprite->width;
2889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      save.height = sprite->height;
2899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      save.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
2909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      save.flags = PNG_IMAGE_FLAG_FAST;
2919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      save.colormap_entries = 0;
2929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (png_image_write_to_stdio(&save, sprite->file, 1/*convert_to_8_bit*/,
2949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett          sprite->buffer, 0/*row_stride*/, NULL/*colormap*/))
2959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
2969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         /* Success; the buffer is no longer needed: */
2979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         free(sprite->buffer);
2989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         sprite->buffer = NULL;
2999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return 1; /* ok */
3009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
3019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      else
3039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         fprintf(stderr, "simpleover: write sprite %s: %s\n", sprite->name,
3049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            save.message);
3059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
3069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   else
3089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      fprintf(stderr, "simpleover: sprite %s: could not allocate tmpfile: %s\n",
3099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         sprite->name, strerror(errno));
3109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return 0; /* fail */
3129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
3139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
3159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettadd_sprite(png_imagep output, png_bytep out_buf, struct sprite *sprite,
3169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   int *argc, const char ***argv)
3179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
3189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Given a --add argument naming this sprite, perform the operations listed
3199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * in the following arguments.  The arguments are expected to have the form
3209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * (x,y), which is just an offset at which to add the sprite to the
3219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * output.
3229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
3239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   while (*argc > 0)
3249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
3259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      char tombstone;
3269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      int x, y;
3279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-')
3299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return 1; /* success */
3309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (sscanf((*argv)[0], "%d,%d%c", &x, &y, &tombstone) == 2)
3329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
3339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         /* Now add the new image into the sprite data, but only if it
3349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett          * will fit.
3359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett          */
3369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (x < 0 || y < 0 ||
3379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             (unsigned)/*SAFE*/x >= output->width ||
3389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             (unsigned)/*SAFE*/y >= output->height ||
3399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             sprite->width > output->width-x ||
3409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             sprite->height > output->height-y)
3419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
3429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            fprintf(stderr, "simpleover: sprite %s @ (%d,%d) outside image\n",
3439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               sprite->name, x, y);
3449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            /* Could just skip this, but for the moment it is an error */
3459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            return 0; /* error */
3469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
3479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         else
3499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
3509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            /* Since we know the sprite fits we can just read it into the
3519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             * output using the simplified API.
3529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             */
3539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            png_image in;
3549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            in.version = PNG_IMAGE_VERSION;
3569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            rewind(sprite->file);
3579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            if (png_image_begin_read_from_stdio(&in, sprite->file))
3599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            {
3609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               in.format = PNG_FORMAT_RGB; /* force compose */
3619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               if (png_image_finish_read(&in, NULL/*background*/,
3639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  out_buf + (y*output->width + x)*3/*RGB*/,
3649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  output->width*3/*row_stride*/,
3659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/))
3669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               {
3679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  ++*argv, --*argc;
3689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  continue;
3699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               }
3709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            }
3719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            /* The read failed: */
3739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            fprintf(stderr, "simpleover: add sprite %s: %s\n", sprite->name,
3749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                in.message);
3759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            return 0; /* error */
3769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
3779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
3789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      else
3809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
3819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         fprintf(stderr, "simpleover: --add='%s': invalid position %s\n",
3829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               sprite->name, (*argv)[0]);
3839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return 0; /* error */
3849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
3859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
3869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return 1; /* ok */
3889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
3899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
3919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettsimpleover_process(png_imagep output, png_bytep out_buf, int argc,
3929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   const char **argv)
3939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
3949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   int result = 1; /* success */
3959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#  define csprites 10/*limit*/
3969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#  define str(a) #a
3979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   int nsprites = 0;
3989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   struct sprite sprites[csprites];
3999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   while (argc > 0)
4019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
4029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      result = 0; /* fail */
4039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (strncmp(argv[0], "--sprite=", 9) == 0)
4059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
4069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         char tombstone;
4079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (nsprites < csprites)
4099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
4109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            int n;
4119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            sprites[nsprites].width = sprites[nsprites].height = 0;
4139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            sprites[nsprites].name[0] = 0;
4149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            n = sscanf(argv[0], "--sprite=%u,%u,%" str(sprite_name_chars) "s%c",
4169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                &sprites[nsprites].width, &sprites[nsprites].height,
4179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                sprites[nsprites].name, &tombstone);
41806f1087a94f1e48298e89d77ccc51a0ced871958Matt Sarett
4199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            if ((n == 2 || n == 3) &&
4209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                sprites[nsprites].width > 0 && sprites[nsprites].height > 0)
4219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            {
4229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               size_t buf_size, tmp;
4239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               /* Default a name if not given. */
4259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               if (sprites[nsprites].name[0] == 0)
4269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  sprintf(sprites[nsprites].name, "sprite-%d", nsprites+1);
4279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               /* Allocate a buffer for the sprite and calculate the buffer
4299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                * size:
4309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                */
4319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               buf_size = sizeof (png_uint_16 [4]);
4329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               buf_size *= sprites[nsprites].width;
4339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               buf_size *= sprites[nsprites].height;
4349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               /* This can overflow a (size_t); check for this: */
4369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               tmp = buf_size;
4379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               tmp /= sprites[nsprites].width;
4389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               tmp /= sprites[nsprites].height;
4399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               if (tmp == sizeof (png_uint_16 [4]))
4419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               {
4429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  sprites[nsprites].buffer = malloc(buf_size);
4439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  /* This buffer must be initialized to transparent: */
4449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  memset(sprites[nsprites].buffer, 0, buf_size);
4459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  if (sprites[nsprites].buffer != NULL)
4479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  {
4489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     sprites[nsprites].file = NULL;
4499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     ++argv, --argc;
4509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     if (create_sprite(sprites+nsprites++, &argc, &argv))
4529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     {
4539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                        result = 1; /* still ok */
4549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                        continue;
4559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     }
4569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     break; /* error */
4589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  }
4599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               }
4609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               /* Overflow, or OOM */
4629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               fprintf(stderr, "simpleover: %s: sprite too large\n", argv[0]);
4639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               break;
4649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            }
4659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            else
4679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            {
4689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               fprintf(stderr, "simpleover: %s: invalid sprite (%u,%u)\n",
4699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  argv[0], sprites[nsprites].width, sprites[nsprites].height);
4709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               break;
4719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            }
4729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
4739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         else
4759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
4769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            fprintf(stderr, "simpleover: %s: too many sprites\n", argv[0]);
4779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            break;
4789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
4799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
4809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      else if (strncmp(argv[0], "--add=", 6) == 0)
4829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
4839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         const char *name = argv[0]+6;
4849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         int isprite = nsprites;
4859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         ++argv, --argc;
4879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         while (--isprite >= 0)
4899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
4909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            if (strcmp(sprites[isprite].name, name) == 0)
4919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            {
4929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               if (!add_sprite(output, out_buf, sprites+isprite, &argc, &argv))
4939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  goto out; /* error in add_sprite */
4949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
49506f1087a94f1e48298e89d77ccc51a0ced871958Matt Sarett               break;
4969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            }
4979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
4989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (isprite < 0) /* sprite not found */
5009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
5019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            fprintf(stderr, "simpleover: --add='%s': sprite not found\n", name);
5029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            break;
5039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
5049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
5059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      else
5079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
5089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         fprintf(stderr, "simpleover: %s: unrecognized operation\n", argv[0]);
5099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         break;
5109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
5119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      result = 1; /* ok  */
5139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
5149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Clean up the cache of sprites: */
5169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettout:
5179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   while (--nsprites >= 0)
5189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
5199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (sprites[nsprites].buffer != NULL)
5209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         free(sprites[nsprites].buffer);
5219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (sprites[nsprites].file != NULL)
5239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         (void)fclose(sprites[nsprites].file);
5249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
5259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return result;
5279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
5289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettint main(int argc, const char **argv)
5309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
5319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   int result = 1; /* default to fail */
5329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (argc >= 2)
5349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
5359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      int argi = 2;
5369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      const char *output = NULL;
5379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      png_image image;
5389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (argc > 2 && argv[2][0] != '-'/*an operation*/)
5409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
5419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         output = argv[2];
5429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         argi = 3;
5439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
5449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      image.version = PNG_IMAGE_VERSION;
5469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      image.opaque = NULL;
5479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (png_image_begin_read_from_file(&image, argv[1]))
5499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
5509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         png_bytep buffer;
5519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         image.format = PNG_FORMAT_RGB; /* 24-bit RGB */
5539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         buffer = malloc(PNG_IMAGE_SIZE(image));
5559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (buffer != NULL)
5579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
5589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            png_color background = {0, 0xff, 0}; /* fully saturated green */
5599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            if (png_image_finish_read(&image, &background, buffer,
5619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               0/*row_stride*/, NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP */))
5629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            {
5639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               /* At this point png_image_finish_read has cleaned up the
5649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                * allocated data in png_image, and only the buffer needs to be
5659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                * freed.
5669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                *
5679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                * Perform the remaining operations:
5689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                */
5699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               if (simpleover_process(&image, buffer, argc-argi, argv+argi))
5709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               {
5719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  /* Write the output: */
5729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  if ((output != NULL &&
5739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                       png_image_write_to_file(&image, output,
5749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                        0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
5759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                        NULL/*colormap*/)) ||
5769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                      (output == NULL &&
5779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                       png_image_write_to_stdio(&image, stdout,
5789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                        0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
5799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                        NULL/*colormap*/)))
5809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     result = 0;
5819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  else
5839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     fprintf(stderr, "simpleover: write %s: %s\n",
5849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                        output == NULL ? "stdout" : output, image.message);
5859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               }
5869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               /* else simpleover_process writes an error message */
5889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            }
5899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            else
5919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               fprintf(stderr, "simpleover: read %s: %s\n", argv[1],
5929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                   image.message);
5939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            free(buffer);
5959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
5969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         else
5989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
5999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            fprintf(stderr, "simpleover: out of memory: %lu bytes\n",
6009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               (unsigned long)PNG_IMAGE_SIZE(image));
6019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            /* This is the only place where a 'free' is required; libpng does
6039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             * the cleanup on error and success, but in this case we couldn't
6049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             * complete the read because of running out of memory.
6059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             */
6069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            png_image_free(&image);
6079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
6089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
6099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      else
6119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
6129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         /* Failed to read the first argument: */
6139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         fprintf(stderr, "simpleover: %s: %s\n", argv[1], image.message);
6149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
6159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
6169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   else
6189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
6199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* Usage message */
6209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      fprintf(stderr,
6219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "simpleover: usage: simpleover background.png [output.png]\n"
6229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  Output 'background.png' as a 24-bit RGB PNG file in 'output.png'\n"
6239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "   or, if not given, stdout.  'background.png' will be composited\n"
6249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "   on fully saturated green.\n"
6259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
6269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  Optionally, before output, process additional PNG files:\n"
6279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
6289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "   --sprite=width,height,name {[--at=x,y] {sprite.png}}\n"
6299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "    Produce a transparent sprite of size (width,height) and with\n"
6309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "     name 'name'.\n"
6319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "    For each sprite.png composite it using a Porter-Duff 'Over'\n"
6329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "     operation at offset (x,y) in the sprite (defaulting to (0,0)).\n"
6339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "     Input PNGs will be truncated to the area of the sprite.\n"
6349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
6359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "   --add='name' {x,y}\n"
6369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "    Optionally, before output, composite a sprite, 'name', which\n"
6379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "     must have been previously produced using --sprite, at each\n"
6389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "     offset (x,y) in the output image.  Each sprite must fit\n"
6399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "     completely within the output image.\n"
6409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
6419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  PNG files are processed in the order they occur on the command\n"
6429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  line and thus the first PNG processed appears as the bottommost\n"
6439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  in the output image.\n");
6449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
6459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return result;
6479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
6489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#endif /* SIMPLIFIED_READ */
649