summaryrefslogtreecommitdiff
path: root/Build/source/libs/libpng/libpng-src/contrib/examples
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/libpng/libpng-src/contrib/examples')
-rw-r--r--Build/source/libs/libpng/libpng-src/contrib/examples/README.txt24
-rw-r--r--Build/source/libs/libpng/libpng-src/contrib/examples/iccfrompng.c185
-rw-r--r--Build/source/libs/libpng/libpng-src/contrib/examples/pngpixel.c371
-rw-r--r--Build/source/libs/libpng/libpng-src/contrib/examples/pngtopng.c95
-rw-r--r--Build/source/libs/libpng/libpng-src/contrib/examples/simpleover.c648
5 files changed, 1323 insertions, 0 deletions
diff --git a/Build/source/libs/libpng/libpng-src/contrib/examples/README.txt b/Build/source/libs/libpng/libpng-src/contrib/examples/README.txt
new file mode 100644
index 00000000000..0525c9d36b4
--- /dev/null
+++ b/Build/source/libs/libpng/libpng-src/contrib/examples/README.txt
@@ -0,0 +1,24 @@
+
+This directory (contrib/examples) contains examples of libpng usage.
+
+NO COPYRIGHT RIGHTS ARE CLAIMED TO ANY OF THE FILES IN THIS DIRECTORY.
+
+To the extent possible under law, the authors have waived all copyright and
+related or neighboring rights to this work. This work is published from:
+United States.
+
+The files may be used freely in any way. The intention is that appropriate
+parts of the files be used in other libpng-using programs without any need for
+the authors of the using code to seek copyright or license from the original
+authors.
+
+The source code and comments in this directory are the original work of the
+people named below. No other person or organization has made contributions to
+the work in this directory.
+
+ORIGINAL AUTHORS
+ The following people have contributed to the code in this directory. None
+ of the people below claim any rights with regard to the contents of this
+ directory.
+
+ John Bowler <jbowler@acm.org>
diff --git a/Build/source/libs/libpng/libpng-src/contrib/examples/iccfrompng.c b/Build/source/libs/libpng/libpng-src/contrib/examples/iccfrompng.c
new file mode 100644
index 00000000000..603037e7097
--- /dev/null
+++ b/Build/source/libs/libpng/libpng-src/contrib/examples/iccfrompng.c
@@ -0,0 +1,185 @@
+/*- iccfrompng
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2011.
+ * 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.
+ *
+ * Extract any icc profiles found in the given PNG files. This is a simple
+ * example of a program that extracts information from the header of a PNG file
+ * without processing the image. Notice that some header information may occur
+ * after the image data. Textual data and comments are an example; the approach
+ * in this file won't work reliably for such data because it only looks for the
+ * information in the section of the file that preceeds the image data.
+ *
+ * Compile and link against libpng and zlib, plus anything else required on the
+ * system you use.
+ *
+ * To use supply a list of PNG files containing iCCP chunks, the chunks will be
+ * extracted to a similarly named file with the extension replaced by 'icc',
+ * which will be overwritten without warning.
+ */
+#include <stdlib.h>
+#include <setjmp.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <png.h>
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) && \
+ defined (PNG_iCCP_SUPPORTED)
+
+
+static int verbose = 1;
+static png_byte no_profile[] = "no profile";
+
+static png_bytep
+extract(FILE *fp, png_uint_32 *proflen)
+{
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+ png_infop info_ptr = NULL;
+ png_bytep result = NULL;
+
+ /* Initialize for error or no profile: */
+ *proflen = 0;
+
+ if (png_ptr == NULL)
+ {
+ fprintf(stderr, "iccfrompng: version library mismatch?\n");
+ return 0;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return 0;
+ }
+
+ png_init_io(png_ptr, fp);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ png_error(png_ptr, "OOM allocating info structure");
+
+ png_read_info(png_ptr, info_ptr);
+
+ {
+ png_charp name;
+ int compression_type;
+ png_bytep profile;
+
+ if (png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile,
+ proflen) & PNG_INFO_iCCP)
+ {
+ result = malloc(*proflen);
+ if (result != NULL)
+ memcpy(result, profile, *proflen);
+
+ else
+ png_error(png_ptr, "OOM allocating profile buffer");
+ }
+
+ else
+ result = no_profile;
+ }
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return result;
+}
+
+static int
+extract_one_file(const char *filename)
+{
+ int result = 0;
+ FILE *fp = fopen(filename, "rb");
+
+ if (fp != NULL)
+ {
+ png_uint_32 proflen = 0;
+ png_bytep profile = extract(fp, &proflen);
+
+ if (profile != NULL && profile != no_profile)
+ {
+ size_t len;
+ char *output;
+
+ {
+ const char *ep = strrchr(filename, '.');
+
+ if (ep != NULL)
+ len = ep-filename;
+
+ else
+ len = strlen(filename);
+ }
+
+ output = malloc(len + 5);
+ if (output != NULL)
+ {
+ FILE *of;
+
+ memcpy(output, filename, len);
+ strcpy(output+len, ".icc");
+
+ of = fopen(output, "wb");
+ if (of != NULL)
+ {
+ if (fwrite(profile, proflen, 1, of) == 1 &&
+ fflush(of) == 0 &&
+ fclose(of) == 0)
+ {
+ if (verbose)
+ printf("%s -> %s\n", filename, output);
+ /* Success return */
+ result = 1;
+ }
+
+ else
+ {
+ fprintf(stderr, "%s: error writing profile\n", output);
+ if (remove(output))
+ fprintf(stderr, "%s: could not remove file\n", output);
+ }
+ }
+
+ else
+ fprintf(stderr, "%s: failed to open output file\n", output);
+
+ free(output);
+ }
+
+ else
+ fprintf(stderr, "%s: OOM allocating string!\n", filename);
+
+ free(profile);
+ }
+
+ else if (verbose && profile == no_profile)
+ printf("%s has no profile\n", filename);
+ }
+
+ else
+ fprintf(stderr, "%s: could not open file\n", filename);
+
+ return result;
+}
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ int extracted = 0;
+
+ for (i=1; i<argc; ++i)
+ {
+ if (strcmp(argv[i], "-q") == 0)
+ verbose = 0;
+
+ else if (extract_one_file(argv[i]))
+ extracted = 1;
+ }
+
+ /* Exit code is true if any extract succeeds */
+ return extracted == 0;
+}
+#endif /* READ && STDIO && iCCP */
diff --git a/Build/source/libs/libpng/libpng-src/contrib/examples/pngpixel.c b/Build/source/libs/libpng/libpng-src/contrib/examples/pngpixel.c
new file mode 100644
index 00000000000..f762379ed3c
--- /dev/null
+++ b/Build/source/libs/libpng/libpng-src/contrib/examples/pngpixel.c
@@ -0,0 +1,371 @@
+/*- pngpixel
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2011.
+ * 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 a single pixel value from a PNG file.
+ *
+ * This code illustrates basic 'by-row' reading of a PNG file using libpng.
+ * Rows are read until a particular pixel is found; the value of this pixel is
+ * then printed on stdout.
+ *
+ * The code illustrates how to do this on interlaced as well as non-interlaced
+ * images. Normally you would call png_set_interlace_handling() to have libpng
+ * deal with the interlace for you, but that obliges you to buffer half of the
+ * image to assemble the interlaced rows. In this code
+ * png_set_interlace_handling() is not called and, instead, the code handles the
+ * interlace passes directly looking for the required pixel.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <setjmp.h> /* required for error handling */
+
+/* Normally use <png.h> here to get the installed libpng, but this is done to
+ * ensure the code picks up the local libpng implementation:
+ */
+#include "../../png.h"
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_SEQUENTIAL_READ_SUPPORTED)
+
+/* Return component 'c' of pixel 'x' from the given row. */
+static unsigned int
+component(png_const_bytep row, png_uint_32 x, unsigned int c,
+ unsigned int bit_depth, unsigned int channels)
+{
+ /* PNG images can be up to 2^31 pixels wide, but this means they can be up to
+ * 2^37 bits wide (for a 64-bit pixel - the largest possible) and hence 2^34
+ * bytes wide. Since the row fitted into memory, however, the following must
+ * work:
+ */
+ png_uint_32 bit_offset_hi = bit_depth * ((x >> 6) * channels);
+ png_uint_32 bit_offset_lo = bit_depth * ((x & 0x3f) * channels + c);
+
+ row = (png_const_bytep)(((PNG_CONST png_byte (*)[8])row) + bit_offset_hi);
+ row += bit_offset_lo >> 3;
+ bit_offset_lo &= 0x07;
+
+ /* PNG pixels are packed into bytes to put the first pixel in the highest
+ * bits of the byte and into two bytes for 16-bit values with the high 8 bits
+ * first, so:
+ */
+ switch (bit_depth)
+ {
+ case 1: return (row[0] >> (7-bit_offset_lo)) & 0x01;
+ case 2: return (row[0] >> (6-bit_offset_lo)) & 0x03;
+ case 4: return (row[0] >> (4-bit_offset_lo)) & 0x0f;
+ case 8: return row[0];
+ case 16: return (row[0] << 8) + row[1];
+ default:
+ /* This should never happen; it indicates a bug in this program or in
+ * libpng itself:
+ */
+ fprintf(stderr, "pngpixel: invalid bit depth %u\n", bit_depth);
+ exit(1);
+ }
+}
+
+/* Print a pixel from a row returned by libpng; determine the row format, find
+ * the pixel, and print the relevant information to stdout.
+ */
+static void
+print_pixel(png_structp png_ptr, png_infop info_ptr, png_const_bytep row,
+ png_uint_32 x)
+{
+ PNG_CONST unsigned int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+
+ switch (png_get_color_type(png_ptr, info_ptr))
+ {
+ case PNG_COLOR_TYPE_GRAY:
+ printf("GRAY %u\n", component(row, x, 0, bit_depth, 1));
+ return;
+
+ /* The palette case is slightly more difficult - the palette and, if
+ * present, the tRNS ('transparency', though the values are really
+ * opacity) data must be read to give the full picture:
+ */
+ case PNG_COLOR_TYPE_PALETTE:
+ {
+ PNG_CONST int index = component(row, x, 0, bit_depth, 1);
+ png_colorp palette = NULL;
+ int num_palette = 0;
+
+ if ((png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette) &
+ PNG_INFO_PLTE) && num_palette > 0 && palette != NULL)
+ {
+ png_bytep trans_alpha = NULL;
+ int num_trans = 0;
+ if ((png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans,
+ NULL) & PNG_INFO_tRNS) && num_trans > 0 &&
+ trans_alpha != NULL)
+ printf("INDEXED %u = %d %d %d %d\n", index,
+ palette[index].red, palette[index].green,
+ palette[index].blue,
+ index < num_trans ? trans_alpha[index] : 255);
+
+ else /* no transparency */
+ printf("INDEXED %u = %d %d %d\n", index,
+ palette[index].red, palette[index].green,
+ palette[index].blue);
+ }
+
+ else
+ printf("INDEXED %u = invalid index\n", index);
+ }
+ return;
+
+ case PNG_COLOR_TYPE_RGB:
+ printf("RGB %u %u %u\n", component(row, x, 0, bit_depth, 3),
+ component(row, x, 1, bit_depth, 3),
+ component(row, x, 2, bit_depth, 3));
+ return;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ printf("GRAY+ALPHA %u %u\n", component(row, x, 0, bit_depth, 2),
+ component(row, x, 1, bit_depth, 2));
+ return;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ printf("RGBA %u %u %u %u\n", component(row, x, 0, bit_depth, 4),
+ component(row, x, 1, bit_depth, 4),
+ component(row, x, 2, bit_depth, 4),
+ component(row, x, 3, bit_depth, 4));
+ return;
+
+ default:
+ png_error(png_ptr, "pngpixel: invalid color type");
+ }
+}
+
+int main(int argc, const char **argv)
+{
+ /* This program uses the default, <setjmp.h> based, libpng error handling
+ * mechanism, therefore any local variable that exists before the call to
+ * setjmp and is changed after the call to setjmp returns successfully must
+ * be declared with 'volatile' to ensure that their values don't get
+ * destroyed by longjmp:
+ */
+ volatile int result = 1/*fail*/;
+
+ if (argc == 4)
+ {
+ long x = atol(argv[1]);
+ long y = atol(argv[2]);
+ FILE *f = fopen(argv[3], "rb");
+ volatile png_bytep row = NULL;
+
+ if (f != NULL)
+ {
+ /* libpng requires a callback function for handling errors; this
+ * callback must not return. The default callback function uses a
+ * stored <setjmp.h> style jmp_buf which is held in a png_struct and
+ * writes error messages to stderr. Creating the png_struct is a
+ * little tricky; just copy the following code.
+ */
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+
+ if (png_ptr != NULL)
+ {
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+
+ if (info_ptr != NULL)
+ {
+ /* Declare stack variables to hold pointers to locally allocated
+ * data.
+ */
+
+ /* Initialize the error control buffer: */
+ if (setjmp(png_jmpbuf(png_ptr)) == 0)
+ {
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_method,
+ compression_method, filter_method;
+ png_bytep row_tmp;
+
+ /* Now associate the recently opened (FILE*) with the default
+ * libpng initialization functions. Sometimes libpng is
+ * compiled without stdio support (it can be difficult to do
+ * in some environments); in that case you will have to write
+ * your own read callback to read data from the (FILE*).
+ */
+ png_init_io(png_ptr, f);
+
+ /* And read the first part of the PNG file - the header and
+ * all the information up to the first pixel.
+ */
+ png_read_info(png_ptr, info_ptr);
+
+ /* This fills in enough information to tell us the width of
+ * each row in bytes, allocate the appropriate amount of
+ * space. In this case png_malloc is used - it will not
+ * return if memory isn't available.
+ */
+ row = png_malloc(png_ptr, png_get_rowbytes(png_ptr,
+ info_ptr));
+
+ /* To avoid the overhead of using a volatile auto copy row_tmp
+ * to a local here - just use row for the png_free below.
+ */
+ row_tmp = row;
+
+ /* All the information we need is in the header is returned by
+ * png_get_IHDR, if this fails we can now use 'png_error' to
+ * signal the error and return control to the setjmp above.
+ */
+ if (png_get_IHDR(png_ptr, info_ptr, &width, &height,
+ &bit_depth, &color_type, &interlace_method,
+ &compression_method, &filter_method))
+ {
+ int passes, pass;
+
+ /* png_set_interlace_handling returns the number of
+ * passes required as well as turning on libpng's
+ * handling, but since we do it ourselves this is
+ * necessary:
+ */
+ switch (interlace_method)
+ {
+ case PNG_INTERLACE_NONE:
+ passes = 1;
+ break;
+
+ case PNG_INTERLACE_ADAM7:
+ passes = PNG_INTERLACE_ADAM7_PASSES;
+ break;
+
+ default:
+ png_error(png_ptr, "pngpixel: unknown interlace");
+ }
+
+ /* Now read the pixels, pass-by-pass, row-by-row: */
+ png_start_read_image(png_ptr);
+
+ for (pass=0; pass<passes; ++pass)
+ {
+ png_uint_32 ystart, xstart, ystep, xstep;
+ png_uint_32 py;
+
+ if (interlace_method == PNG_INTERLACE_ADAM7)
+ {
+ /* Sometimes the whole pass is empty because the
+ * image is too narrow or too short. libpng
+ * expects to be called for each row that is
+ * present in the pass, so it may be necessary to
+ * skip the loop below (over py) if the image is
+ * too narrow.
+ */
+ if (PNG_PASS_COLS(width, pass) == 0)
+ continue;
+
+ /* We need the starting pixel and the offset
+ * between each pixel in this pass; use the macros
+ * in png.h:
+ */
+ xstart = PNG_PASS_START_COL(pass);
+ ystart = PNG_PASS_START_ROW(pass);
+ xstep = PNG_PASS_COL_OFFSET(pass);
+ ystep = PNG_PASS_ROW_OFFSET(pass);
+ }
+
+ else
+ {
+ ystart = xstart = 0;
+ ystep = xstep = 1;
+ }
+
+ /* To find the pixel, loop over 'py' for each pass
+ * reading a row and then checking to see if it
+ * contains the pixel.
+ */
+ for (py = ystart; py < height; py += ystep)
+ {
+ png_uint_32 px, ppx;
+
+ /* png_read_row takes two pointers. When libpng
+ * handles the interlace the first is filled in
+ * pixel-by-pixel, and the second receives the same
+ * pixels but they are replicated across the
+ * unwritten pixels so far for each pass. When we
+ * do the interlace, however, they just contain
+ * the pixels from the interlace pass - giving
+ * both is wasteful and pointless, so we pass a
+ * NULL pointer.
+ */
+ png_read_row(png_ptr, row_tmp, NULL);
+
+ /* Now find the pixel if it is in this row; there
+ * are, of course, much better ways of doing this
+ * than using a for loop:
+ */
+ if (y == py) for (px = xstart, ppx = 0;
+ px < width; px += xstep, ++ppx) if (x == px)
+ {
+ /* 'ppx' is the index of the pixel in the row
+ * buffer.
+ */
+ print_pixel(png_ptr, info_ptr, row_tmp, ppx);
+
+ /* Now terminate the loops early - we have
+ * found and handled the required data.
+ */
+ goto pass_loop_end;
+ } /* x loop */
+ } /* y loop */
+ } /* pass loop */
+
+ /* Finally free the temporary buffer: */
+ pass_loop_end:
+ row = NULL;
+ png_free(png_ptr, row_tmp);
+ }
+
+ else
+ png_error(png_ptr, "pngpixel: png_get_IHDR failed");
+
+ }
+
+ else
+ {
+ /* Else libpng has raised an error. An error message has
+ * already been output, so it is only necessary to clean up
+ * locally allocated data:
+ */
+ if (row != NULL)
+ {
+ /* The default implementation of png_free never errors out
+ * (it just crashes if something goes wrong), but the safe
+ * way of using it is still to clear 'row' before calling
+ * png_free:
+ */
+ png_bytep row_tmp = row;
+ row = NULL;
+ png_free(png_ptr, row_tmp);
+ }
+ }
+
+ png_destroy_info_struct(png_ptr, &info_ptr);
+ }
+
+ else
+ fprintf(stderr, "pngpixel: out of memory allocating png_info\n");
+
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ }
+
+ else
+ fprintf(stderr, "pngpixel: out of memory allocating png_struct\n");
+ }
+
+ else
+ fprintf(stderr, "pngpixel: %s: could not open file\n", argv[3]);
+ }
+
+ else
+ /* Wrong number of arguments */
+ fprintf(stderr, "pngpixel: usage: pngpixel x y png-file\n");
+
+ return result;
+}
+#endif /* READ && SEQUENTIAL_READ */
diff --git a/Build/source/libs/libpng/libpng-src/contrib/examples/pngtopng.c b/Build/source/libs/libpng/libpng-src/contrib/examples/pngtopng.c
new file mode 100644
index 00000000000..4acf6b3ad4d
--- /dev/null
+++ b/Build/source/libs/libpng/libpng-src/contrib/examples/pngtopng.c
@@ -0,0 +1,95 @@
+/*- pngtopng
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2011.
+ * 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 a PNG and write it out in a fixed format, using the 'simplified API'
+ * that was introduced in libpng-1.6.0.
+ *
+ * This sample code is just the code from the top of 'example.c' with some error
+ * handling added. See example.c for more comments.
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.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:
+ */
+#include "../../png.h"
+#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && \
+ defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
+
+int main(int argc, const char **argv)
+{
+ int result = 1;
+
+ if (argc == 3)
+ {
+ png_image image;
+
+ /* Only the image structure version number needs to be set. */
+ memset(&image, 0, sizeof image);
+ image.version = PNG_IMAGE_VERSION;
+
+ if (png_image_begin_read_from_file(&image, argv[1]))
+ {
+ png_bytep buffer;
+
+ /* Change this to try different formats! If you set a colormap format
+ * then you must also supply a colormap below.
+ */
+ image.format = PNG_FORMAT_RGBA;
+
+ 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 */))
+ {
+ if (png_image_write_to_file(&image, argv[2],
+ 0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
+ NULL/*colormap*/))
+ result = 0;
+
+ else
+ fprintf(stderr, "pngtopng: write %s: %s\n", argv[2],
+ image.message);
+
+ free(buffer);
+ }
+
+ else
+ {
+ fprintf(stderr, "pngtopng: read %s: %s\n", argv[1],
+ image.message);
+
+ /* 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
+ fprintf(stderr, "pngtopng: out of memory: %lu bytes\n",
+ (unsigned long)PNG_IMAGE_SIZE(image));
+ }
+
+ else
+ /* Failed to read the first argument: */
+ fprintf(stderr, "pngtopng: %s: %s\n", argv[1], image.message);
+ }
+
+ else
+ /* Wrong number of arguments */
+ fprintf(stderr, "pngtopng: usage: pngtopng input-file output-file\n");
+
+ return result;
+}
+#endif /* READ && WRITE */
diff --git a/Build/source/libs/libpng/libpng-src/contrib/examples/simpleover.c b/Build/source/libs/libpng/libpng-src/contrib/examples/simpleover.c
new file mode 100644
index 00000000000..59dd313688c
--- /dev/null
+++ b/Build/source/libs/libpng/libpng-src/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 */