Index: third_party/libpng/contrib/examples/simpleover.c |
diff --git a/third_party/libpng/contrib/examples/simpleover.c b/third_party/libpng/contrib/examples/simpleover.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..59dd313688c190afd5d6b9d33bd78935b51a22b2 |
--- /dev/null |
+++ b/third_party/libpng/contrib/examples/simpleover.c |
@@ -0,0 +1,648 @@ |
+/*- simpleover |
+ * |
+ * COPYRIGHT: Written by John Cunningham Bowler, 2015. |
+ * To the extent possible under law, the author has waived all copyright and |
+ * related or neighboring rights to this work. This work is published from: |
+ * United States. |
+ * |
+ * Read several PNG files, which should have an alpha channel or transparency |
+ * information, and composite them together to produce one or more 16-bit linear |
+ * RGBA intermediates. This involves doing the correct 'over' composition to |
+ * combine the alpha channels and corresponding data. |
+ * |
+ * Finally read an output (background) PNG using the 24-bit RGB format (the |
+ * PNG will be composited on green (#00ff00) by default if it has an alpha |
+ * channel), and apply the intermediate image generated above to specified |
+ * locations in the image. |
+ * |
+ * The command line has the general format: |
+ * |
+ * simpleover <background.png> [output.png] |
+ * {--sprite=width,height,name {[--at=x,y] {sprite.png}}} |
+ * {--add=name {x,y}} |
+ * |
+ * The --sprite and --add options may occur multiple times. They are executed |
+ * in order. --add may refer to any sprite already read. |
+ * |
+ * This code is intended to show how to composite multiple images together |
+ * correctly. Apart from the libpng Simplified API the only work done in here |
+ * is to combine multiple input PNG images into a single sprite; this involves |
+ * a Porter-Duff 'over' operation and the input PNG images may, as a result, |
+ * be regarded as being layered one on top of the other with the first (leftmost |
+ * on the command line) being at the bottom and the last on the top. |
+ */ |
+#include <stddef.h> |
+#include <stdlib.h> |
+#include <string.h> |
+#include <stdio.h> |
+#include <errno.h> |
+ |
+/* Normally use <png.h> here to get the installed libpng, but this is done to |
+ * ensure the code picks up the local libpng implementation, so long as this |
+ * file is linked against a sufficiently recent libpng (1.6+) it is ok to |
+ * change this to <png.h>: |
+ */ |
+#include "../../png.h" |
+ |
+#ifdef PNG_SIMPLIFIED_READ_SUPPORTED |
+ |
+#define sprite_name_chars 15 |
+struct sprite { |
+ FILE *file; |
+ png_uint_16p buffer; |
+ unsigned int width; |
+ unsigned int height; |
+ char name[sprite_name_chars+1]; |
+}; |
+ |
+#if 0 /* div by 65535 test program */ |
+#include <math.h> |
+#include <stdio.h> |
+ |
+int main(void) { |
+ double err = 0; |
+ unsigned int xerr = 0; |
+ unsigned int r = 32769; |
+ { |
+ unsigned int x = 0; |
+ |
+ do { |
+ unsigned int t = x + (x >> 16) /*+ (x >> 31)*/ + r; |
+ double v = x, errtest; |
+ |
+ if (t < x) { |
+ fprintf(stderr, "overflow: %u+%u -> %u\n", x, r, t); |
+ return 1; |
+ } |
+ |
+ v /= 65535; |
+ errtest = v; |
+ t >>= 16; |
+ errtest -= t; |
+ |
+ if (errtest > err) { |
+ err = errtest; |
+ xerr = x; |
+ |
+ if (errtest >= .5) { |
+ fprintf(stderr, "error: %u/65535 = %f, not %u, error %f\n", |
+ x, v, t, errtest); |
+ return 0; |
+ } |
+ } |
+ } while (++x <= 65535U*65535U); |
+ } |
+ |
+ printf("error %f @ %u\n", err, xerr); |
+ |
+ return 0; |
+} |
+#endif /* div by 65535 test program */ |
+ |
+static void |
+sprite_op(const struct sprite *sprite, int x_offset, int y_offset, |
+ png_imagep image, const png_uint_16 *buffer) |
+{ |
+ /* This is where the Porter-Duff 'Over' operator is evaluated; change this |
+ * code to change the operator (this could be parameterized). Any other |
+ * image processing operation could be used here. |
+ */ |
+ |
+ |
+ /* Check for an x or y offset that pushes any part of the image beyond the |
+ * right or bottom of the sprite: |
+ */ |
+ if ((y_offset < 0 || (unsigned)/*SAFE*/y_offset < sprite->height) && |
+ (x_offset < 0 || (unsigned)/*SAFE*/x_offset < sprite->width)) |
+ { |
+ unsigned int y = 0; |
+ |
+ if (y_offset < 0) |
+ y = -y_offset; /* Skip to first visible row */ |
+ |
+ do |
+ { |
+ unsigned int x = 0; |
+ |
+ if (x_offset < 0) |
+ x = -x_offset; |
+ |
+ do |
+ { |
+ /* In and out are RGBA values, so: */ |
+ const png_uint_16 *in_pixel = buffer + (y * image->width + x)*4; |
+ png_uint_32 in_alpha = in_pixel[3]; |
+ |
+ /* This is the optimized Porter-Duff 'Over' operation, when the |
+ * input alpha is 0 the output is not changed. |
+ */ |
+ if (in_alpha > 0) |
+ { |
+ png_uint_16 *out_pixel = sprite->buffer + |
+ ((y+y_offset) * sprite->width + (x+x_offset))*4; |
+ |
+ /* This is the weight to apply to the output: */ |
+ in_alpha = 65535-in_alpha; |
+ |
+ if (in_alpha > 0) |
+ { |
+ /* The input must be composed onto the output. This means |
+ * multiplying the current output pixel value by the inverse |
+ * of the input alpha (1-alpha). A division is required but |
+ * it is by the constant 65535. Approximate this as: |
+ * |
+ * (x + (x >> 16) + 32769) >> 16; |
+ * |
+ * This is exact (and does not overflow) for all values of |
+ * x in the range 0..65535*65535. (Note that the calculation |
+ * produces the closest integer; the maximum error is <0.5). |
+ */ |
+ png_uint_32 tmp; |
+ |
+# define compose(c)\ |
+ tmp = out_pixel[c] * in_alpha;\ |
+ tmp = (tmp + (tmp >> 16) + 32769) >> 16;\ |
+ out_pixel[c] = tmp + in_pixel[c] |
+ |
+ /* The following is very vectorizable... */ |
+ compose(0); |
+ compose(1); |
+ compose(2); |
+ compose(3); |
+ } |
+ |
+ else |
+ out_pixel[0] = in_pixel[0], |
+ out_pixel[1] = in_pixel[1], |
+ out_pixel[2] = in_pixel[2], |
+ out_pixel[3] = in_pixel[3]; |
+ } |
+ } |
+ while (++x < image->width); |
+ } |
+ while (++y < image->height); |
+ } |
+} |
+ |
+static int |
+create_sprite(struct sprite *sprite, int *argc, const char ***argv) |
+{ |
+ /* Read the arguments and create this sprite. The sprite buffer has already |
+ * been allocated. This reads the input PNGs one by one in linear format, |
+ * composes them onto the sprite buffer (the code in the function above) |
+ * then saves the result, converting it on the fly to PNG RGBA 8-bit format. |
+ */ |
+ while (*argc > 0) |
+ { |
+ char tombstone; |
+ int x = 0, y = 0; |
+ |
+ if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-') |
+ { |
+ /* The only supported option is --at. */ |
+ if (sscanf((*argv)[0], "--at=%d,%d%c", &x, &y, &tombstone) != 2) |
+ break; /* success; caller will parse this option */ |
+ |
+ ++*argv, --*argc; |
+ } |
+ |
+ else |
+ { |
+ /* The argument has to be a file name */ |
+ png_image image; |
+ |
+ image.version = PNG_IMAGE_VERSION; |
+ image.opaque = NULL; |
+ |
+ if (png_image_begin_read_from_file(&image, (*argv)[0])) |
+ { |
+ png_uint_16p buffer; |
+ |
+ image.format = PNG_FORMAT_LINEAR_RGB_ALPHA; |
+ |
+ buffer = malloc(PNG_IMAGE_SIZE(image)); |
+ |
+ if (buffer != NULL) |
+ { |
+ if (png_image_finish_read(&image, NULL/*background*/, buffer, |
+ 0/*row_stride*/, |
+ NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/)) |
+ { |
+ /* This is the place where the Porter-Duff 'Over' operator |
+ * needs to be done by this code. In fact, any image |
+ * processing required can be done here; the data is in |
+ * the correct format (linear, 16-bit) and source and |
+ * destination are in memory. |
+ */ |
+ sprite_op(sprite, x, y, &image, buffer); |
+ free(buffer); |
+ ++*argv, --*argc; |
+ /* And continue to the next argument */ |
+ continue; |
+ } |
+ |
+ else |
+ { |
+ free(buffer); |
+ fprintf(stderr, "simpleover: read %s: %s\n", (*argv)[0], |
+ image.message); |
+ } |
+ } |
+ |
+ else |
+ { |
+ fprintf(stderr, "simpleover: out of memory: %lu bytes\n", |
+ (unsigned long)PNG_IMAGE_SIZE(image)); |
+ |
+ /* png_image_free must be called if we abort the Simplified API |
+ * read because of a problem detected in this code. If problems |
+ * are detected in the Simplified API it cleans up itself. |
+ */ |
+ png_image_free(&image); |
+ } |
+ } |
+ |
+ else |
+ { |
+ /* Failed to read the first argument: */ |
+ fprintf(stderr, "simpleover: %s: %s\n", (*argv)[0], image.message); |
+ } |
+ |
+ return 0; /* failure */ |
+ } |
+ } |
+ |
+ /* All the sprite operations have completed successfully. Save the RGBA |
+ * buffer as a PNG using the simplified write API. |
+ */ |
+ sprite->file = tmpfile(); |
+ |
+ if (sprite->file != NULL) |
+ { |
+ png_image save; |
+ |
+ memset(&save, 0, sizeof save); |
+ save.version = PNG_IMAGE_VERSION; |
+ save.opaque = NULL; |
+ save.width = sprite->width; |
+ save.height = sprite->height; |
+ save.format = PNG_FORMAT_LINEAR_RGB_ALPHA; |
+ save.flags = PNG_IMAGE_FLAG_FAST; |
+ save.colormap_entries = 0; |
+ |
+ if (png_image_write_to_stdio(&save, sprite->file, 1/*convert_to_8_bit*/, |
+ sprite->buffer, 0/*row_stride*/, NULL/*colormap*/)) |
+ { |
+ /* Success; the buffer is no longer needed: */ |
+ free(sprite->buffer); |
+ sprite->buffer = NULL; |
+ return 1; /* ok */ |
+ } |
+ |
+ else |
+ fprintf(stderr, "simpleover: write sprite %s: %s\n", sprite->name, |
+ save.message); |
+ } |
+ |
+ else |
+ fprintf(stderr, "simpleover: sprite %s: could not allocate tmpfile: %s\n", |
+ sprite->name, strerror(errno)); |
+ |
+ return 0; /* fail */ |
+} |
+ |
+static int |
+add_sprite(png_imagep output, png_bytep out_buf, struct sprite *sprite, |
+ int *argc, const char ***argv) |
+{ |
+ /* Given a --add argument naming this sprite, perform the operations listed |
+ * in the following arguments. The arguments are expected to have the form |
+ * (x,y), which is just an offset at which to add the sprite to the |
+ * output. |
+ */ |
+ while (*argc > 0) |
+ { |
+ char tombstone; |
+ int x, y; |
+ |
+ if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-') |
+ return 1; /* success */ |
+ |
+ if (sscanf((*argv)[0], "%d,%d%c", &x, &y, &tombstone) == 2) |
+ { |
+ /* Now add the new image into the sprite data, but only if it |
+ * will fit. |
+ */ |
+ if (x < 0 || y < 0 || |
+ (unsigned)/*SAFE*/x >= output->width || |
+ (unsigned)/*SAFE*/y >= output->height || |
+ sprite->width > output->width-x || |
+ sprite->height > output->height-y) |
+ { |
+ fprintf(stderr, "simpleover: sprite %s @ (%d,%d) outside image\n", |
+ sprite->name, x, y); |
+ /* Could just skip this, but for the moment it is an error */ |
+ return 0; /* error */ |
+ } |
+ |
+ else |
+ { |
+ /* Since we know the sprite fits we can just read it into the |
+ * output using the simplified API. |
+ */ |
+ png_image in; |
+ |
+ in.version = PNG_IMAGE_VERSION; |
+ rewind(sprite->file); |
+ |
+ if (png_image_begin_read_from_stdio(&in, sprite->file)) |
+ { |
+ in.format = PNG_FORMAT_RGB; /* force compose */ |
+ |
+ if (png_image_finish_read(&in, NULL/*background*/, |
+ out_buf + (y*output->width + x)*3/*RGB*/, |
+ output->width*3/*row_stride*/, |
+ NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/)) |
+ { |
+ ++*argv, --*argc; |
+ continue; |
+ } |
+ } |
+ |
+ /* The read failed: */ |
+ fprintf(stderr, "simpleover: add sprite %s: %s\n", sprite->name, |
+ in.message); |
+ return 0; /* error */ |
+ } |
+ } |
+ |
+ else |
+ { |
+ fprintf(stderr, "simpleover: --add='%s': invalid position %s\n", |
+ sprite->name, (*argv)[0]); |
+ return 0; /* error */ |
+ } |
+ } |
+ |
+ return 1; /* ok */ |
+} |
+ |
+static int |
+simpleover_process(png_imagep output, png_bytep out_buf, int argc, |
+ const char **argv) |
+{ |
+ int result = 1; /* success */ |
+# define csprites 10/*limit*/ |
+# define str(a) #a |
+ int nsprites = 0; |
+ struct sprite sprites[csprites]; |
+ |
+ while (argc > 0) |
+ { |
+ result = 0; /* fail */ |
+ |
+ if (strncmp(argv[0], "--sprite=", 9) == 0) |
+ { |
+ char tombstone; |
+ |
+ if (nsprites < csprites) |
+ { |
+ int n; |
+ |
+ sprites[nsprites].width = sprites[nsprites].height = 0; |
+ sprites[nsprites].name[0] = 0; |
+ |
+ n = sscanf(argv[0], "--sprite=%u,%u,%" str(sprite_name_chars) "s%c", |
+ &sprites[nsprites].width, &sprites[nsprites].height, |
+ sprites[nsprites].name, &tombstone); |
+ |
+ if ((n == 2 || n == 3) && |
+ sprites[nsprites].width > 0 && sprites[nsprites].height > 0) |
+ { |
+ size_t buf_size, tmp; |
+ |
+ /* Default a name if not given. */ |
+ if (sprites[nsprites].name[0] == 0) |
+ sprintf(sprites[nsprites].name, "sprite-%d", nsprites+1); |
+ |
+ /* Allocate a buffer for the sprite and calculate the buffer |
+ * size: |
+ */ |
+ buf_size = sizeof (png_uint_16 [4]); |
+ buf_size *= sprites[nsprites].width; |
+ buf_size *= sprites[nsprites].height; |
+ |
+ /* This can overflow a (size_t); check for this: */ |
+ tmp = buf_size; |
+ tmp /= sprites[nsprites].width; |
+ tmp /= sprites[nsprites].height; |
+ |
+ if (tmp == sizeof (png_uint_16 [4])) |
+ { |
+ sprites[nsprites].buffer = malloc(buf_size); |
+ /* This buffer must be initialized to transparent: */ |
+ memset(sprites[nsprites].buffer, 0, buf_size); |
+ |
+ if (sprites[nsprites].buffer != NULL) |
+ { |
+ sprites[nsprites].file = NULL; |
+ ++argv, --argc; |
+ |
+ if (create_sprite(sprites+nsprites++, &argc, &argv)) |
+ { |
+ result = 1; /* still ok */ |
+ continue; |
+ } |
+ |
+ break; /* error */ |
+ } |
+ } |
+ |
+ /* Overflow, or OOM */ |
+ fprintf(stderr, "simpleover: %s: sprite too large\n", argv[0]); |
+ break; |
+ } |
+ |
+ else |
+ { |
+ fprintf(stderr, "simpleover: %s: invalid sprite (%u,%u)\n", |
+ argv[0], sprites[nsprites].width, sprites[nsprites].height); |
+ break; |
+ } |
+ } |
+ |
+ else |
+ { |
+ fprintf(stderr, "simpleover: %s: too many sprites\n", argv[0]); |
+ break; |
+ } |
+ } |
+ |
+ else if (strncmp(argv[0], "--add=", 6) == 0) |
+ { |
+ const char *name = argv[0]+6; |
+ int isprite = nsprites; |
+ |
+ ++argv, --argc; |
+ |
+ while (--isprite >= 0) |
+ { |
+ if (strcmp(sprites[isprite].name, name) == 0) |
+ { |
+ if (!add_sprite(output, out_buf, sprites+isprite, &argc, &argv)) |
+ goto out; /* error in add_sprite */ |
+ |
+ break; |
+ } |
+ } |
+ |
+ if (isprite < 0) /* sprite not found */ |
+ { |
+ fprintf(stderr, "simpleover: --add='%s': sprite not found\n", name); |
+ break; |
+ } |
+ } |
+ |
+ else |
+ { |
+ fprintf(stderr, "simpleover: %s: unrecognized operation\n", argv[0]); |
+ break; |
+ } |
+ |
+ result = 1; /* ok */ |
+ } |
+ |
+ /* Clean up the cache of sprites: */ |
+out: |
+ while (--nsprites >= 0) |
+ { |
+ if (sprites[nsprites].buffer != NULL) |
+ free(sprites[nsprites].buffer); |
+ |
+ if (sprites[nsprites].file != NULL) |
+ (void)fclose(sprites[nsprites].file); |
+ } |
+ |
+ return result; |
+} |
+ |
+int main(int argc, const char **argv) |
+{ |
+ int result = 1; /* default to fail */ |
+ |
+ if (argc >= 2) |
+ { |
+ int argi = 2; |
+ const char *output = NULL; |
+ png_image image; |
+ |
+ if (argc > 2 && argv[2][0] != '-'/*an operation*/) |
+ { |
+ output = argv[2]; |
+ argi = 3; |
+ } |
+ |
+ image.version = PNG_IMAGE_VERSION; |
+ image.opaque = NULL; |
+ |
+ if (png_image_begin_read_from_file(&image, argv[1])) |
+ { |
+ png_bytep buffer; |
+ |
+ image.format = PNG_FORMAT_RGB; /* 24-bit RGB */ |
+ |
+ buffer = malloc(PNG_IMAGE_SIZE(image)); |
+ |
+ if (buffer != NULL) |
+ { |
+ png_color background = {0, 0xff, 0}; /* fully saturated green */ |
+ |
+ if (png_image_finish_read(&image, &background, buffer, |
+ 0/*row_stride*/, NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP */)) |
+ { |
+ /* At this point png_image_finish_read has cleaned up the |
+ * allocated data in png_image, and only the buffer needs to be |
+ * freed. |
+ * |
+ * Perform the remaining operations: |
+ */ |
+ if (simpleover_process(&image, buffer, argc-argi, argv+argi)) |
+ { |
+ /* Write the output: */ |
+ if ((output != NULL && |
+ png_image_write_to_file(&image, output, |
+ 0/*convert_to_8bit*/, buffer, 0/*row_stride*/, |
+ NULL/*colormap*/)) || |
+ (output == NULL && |
+ png_image_write_to_stdio(&image, stdout, |
+ 0/*convert_to_8bit*/, buffer, 0/*row_stride*/, |
+ NULL/*colormap*/))) |
+ result = 0; |
+ |
+ else |
+ fprintf(stderr, "simpleover: write %s: %s\n", |
+ output == NULL ? "stdout" : output, image.message); |
+ } |
+ |
+ /* else simpleover_process writes an error message */ |
+ } |
+ |
+ else |
+ fprintf(stderr, "simpleover: read %s: %s\n", argv[1], |
+ image.message); |
+ |
+ free(buffer); |
+ } |
+ |
+ else |
+ { |
+ fprintf(stderr, "simpleover: out of memory: %lu bytes\n", |
+ (unsigned long)PNG_IMAGE_SIZE(image)); |
+ |
+ /* This is the only place where a 'free' is required; libpng does |
+ * the cleanup on error and success, but in this case we couldn't |
+ * complete the read because of running out of memory. |
+ */ |
+ png_image_free(&image); |
+ } |
+ } |
+ |
+ else |
+ { |
+ /* Failed to read the first argument: */ |
+ fprintf(stderr, "simpleover: %s: %s\n", argv[1], image.message); |
+ } |
+ } |
+ |
+ else |
+ { |
+ /* Usage message */ |
+ fprintf(stderr, |
+ "simpleover: usage: simpleover background.png [output.png]\n" |
+ " Output 'background.png' as a 24-bit RGB PNG file in 'output.png'\n" |
+ " or, if not given, stdout. 'background.png' will be composited\n" |
+ " on fully saturated green.\n" |
+ "\n" |
+ " Optionally, before output, process additional PNG files:\n" |
+ "\n" |
+ " --sprite=width,height,name {[--at=x,y] {sprite.png}}\n" |
+ " Produce a transparent sprite of size (width,height) and with\n" |
+ " name 'name'.\n" |
+ " For each sprite.png composite it using a Porter-Duff 'Over'\n" |
+ " operation at offset (x,y) in the sprite (defaulting to (0,0)).\n" |
+ " Input PNGs will be truncated to the area of the sprite.\n" |
+ "\n" |
+ " --add='name' {x,y}\n" |
+ " Optionally, before output, composite a sprite, 'name', which\n" |
+ " must have been previously produced using --sprite, at each\n" |
+ " offset (x,y) in the output image. Each sprite must fit\n" |
+ " completely within the output image.\n" |
+ "\n" |
+ " PNG files are processed in the order they occur on the command\n" |
+ " line and thus the first PNG processed appears as the bottommost\n" |
+ " in the output image.\n"); |
+ } |
+ |
+ return result; |
+} |
+#endif /* SIMPLIFIED_READ */ |