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
50struct 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
62int 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
102static void
103sprite_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
187static int
188create_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
314static int
315add_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
390static int
391simpleover_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: */
516out:
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
529int 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