Home | History | Annotate | Download | only in examples
      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 */
    649