[master] Imported from libpng-1.6.18.tar

This commit is contained in:
Glenn Randers-Pehrson
2015-07-22 22:36:43 -05:00
parent 2b667e4923
commit 287fb89248
54 changed files with 3139 additions and 1878 deletions

View File

@@ -1,7 +1,7 @@
OPERATING SYSTEM SPECIFIC ARM NEON DETECTION
--------------------------------------------
Detection of the ability to exexcute ARM NEON on an ARM processor requires
Detection of the ability to execute ARM NEON on an ARM processor requires
operating system support. (The information is not available in user mode.)
HOW TO USE THIS

View File

@@ -26,6 +26,10 @@
#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";
@@ -178,3 +182,4 @@ main(int argc, char **argv)
/* Exit code is true if any extract succeeds */
return extracted == 0;
}
#endif /* READ && STDIO && iCCP */

View File

@@ -27,6 +27,8 @@
*/
#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,
@@ -366,3 +368,4 @@ int main(int argc, const char **argv)
return result;
}
#endif /* READ && SEQUENTIAL_READ */

View File

@@ -20,6 +20,8 @@
* 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)
{
@@ -90,3 +92,4 @@ int main(int argc, const char **argv)
return result;
}
#endif /* READ && WRITE */

View File

@@ -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 */

View File

@@ -1,8 +1,8 @@
/* pngimage.c
*
* Copyright (c) 2014 John Cunningham Bowler
* Copyright (c) 2015 John Cunningham Bowler
*
* Last changed in libpng 1.6.10 [March 6, 2014]
* Last changed in libpng 1.6.18 [July 23, 2015]
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
@@ -1120,8 +1120,8 @@ compare_read(struct display *dp, int applied_transforms)
{
int b;
case 16: /* Two bytes per component, bit-endian */
for (b = (bpp >> 4); b > 0; )
case 16: /* Two bytes per component, big-endian */
for (b = (bpp >> 4); b > 0; --b)
{
unsigned int sig = (unsigned int)(0xffff0000 >> sig_bits[b]);

View File

@@ -1,9 +1,9 @@
/*-
* pngstest.c
*
* Copyright (c) 2013-2014 John Cunningham Bowler
* Copyright (c) 2013-2015 John Cunningham Bowler
*
* Last changed in libpng 1.6.16 [December 22, 2014]
* Last changed in libpng 1.6.18 [July 23, 2015]
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
@@ -615,7 +615,7 @@ freeimage(Image *image)
if (image->tmpfile_name[0] != 0 && (image->opts & KEEP_TMPFILES) == 0)
{
remove(image->tmpfile_name);
(void)remove(image->tmpfile_name);
image->tmpfile_name[0] = 0;
}
}
@@ -2828,7 +2828,7 @@ compare_two_images(Image *a, Image *b, int via_linear,
else if (y >= b->image.colormap_entries)
{
if ((a->opts & ACCUMULATE) == 0)
if ((b->opts & ACCUMULATE) == 0)
{
char pindex[9];
sprintf(pindex, "%lu[%lu]", (unsigned long)y,
@@ -3175,7 +3175,9 @@ read_one_file(Image *image)
if (cb > 0)
{
#ifndef __COVERITY__
if ((unsigned long int)cb <= (size_t)~(size_t)0)
#endif
{
png_bytep b = voidcast(png_bytep, malloc((size_t)cb));
@@ -3243,8 +3245,41 @@ write_one_file(Image *output, Image *image, int convert_to_8bit)
if (image->opts & USE_STDIO)
{
#ifndef PNG_USE_MKSTEMP
FILE *f = tmpfile();
#else
/* Experimental. Coverity says tmpfile() is insecure because it
* generates predictable names.
*
* It is possible to satisfy Coverity by using mkstemp(); however,
* any platform supporting mkstemp() undoubtedly has a secure tmpfile()
* implementation as well, and doesn't need the fix. Note that
* the fix won't work on platforms that don't support mkstemp().
*
* https://www.securecoding.cert.org/confluence/display/c/
* FIO21-C.+Do+not+create+temporary+files+in+shared+directories
* says that most historic implementations of tmpfile() provide
* only a limited number of possible temporary file names
* (usually 26) before file names are recycled. That article also
* provides a secure solution that unfortunately depends upon mkstemp().
*/
char tmpfile[] = "pngstest-XXXXXX";
int filedes;
FILE *f;
umask(0177);
filedes = mkstemp(tmpfile);
if (filedes < 0)
f = NULL;
else
{
f = fdopen(filedes,"w+");
/* Hide the filename immediately and ensure that the file does
* not exist after the program ends
*/
(void) unlink(tmpfile);
}
#endif
if (f != NULL)
{
if (png_image_write_to_stdio(&image->image, f, convert_to_8bit,
@@ -3588,7 +3623,7 @@ main(int argc, char **argv)
}
/* Safe: checked above */
strcpy(tmpf, argv[c]);
strncpy(tmpf, argv[c], sizeof (tmpf)-1);
}
else

View File

@@ -1,7 +1,7 @@
/* pngvalid.c - validate libpng by constructing then reading png files.
*
* Last changed in libpng 1.6.17 [March 26, 2015]
* Last changed in libpng 1.6.18 [July 23, 2015]
* Copyright (c) 2014-2015 Glenn Randers-Pehrson
* Written by John Cunningham Bowler
*
@@ -258,7 +258,7 @@ make_four_random_bytes(png_uint_32* seed, png_bytep bytes)
make_random_bytes(seed, bytes, 4);
}
#ifdef PNG_READ_SUPPORTED
#if defined PNG_READ_SUPPORTED || defined PNG_WRITE_tRNS_SUPPORTED
static void
randomize(void *pv, size_t size)
{
@@ -267,7 +267,9 @@ randomize(void *pv, size_t size)
}
#define RANDOMIZE(this) randomize(&(this), sizeof (this))
#endif /* READ || WRITE_tRNS */
#ifdef PNG_READ_SUPPORTED
static unsigned int
random_mod(unsigned int max)
{
@@ -295,7 +297,8 @@ random_choice(void)
/* A numeric ID based on PNG file characteristics. The 'do_interlace' field
* simply records whether pngvalid did the interlace itself or whether it
* was done by libpng. Width and height must be less than 256. 'palette' is an
* index of the palette to use for formats with a palette (0 otherwise.)
* index of the palette to use for formats with a palette otherwise a boolean
* indicating if a tRNS chunk was generated.
*/
#define FILEID(col, depth, palette, interlace, width, height, do_interlace) \
((png_uint_32)((col) + ((depth)<<3) + ((palette)<<8) + ((interlace)<<13) + \
@@ -316,12 +319,16 @@ standard_name(char *buffer, size_t bufsize, size_t pos, png_byte colour_type,
png_uint_32 w, png_uint_32 h, int do_interlace)
{
pos = safecat(buffer, bufsize, pos, colour_types[colour_type]);
if (npalette > 0)
if (colour_type == 3) /* must have a palette */
{
pos = safecat(buffer, bufsize, pos, "[");
pos = safecatn(buffer, bufsize, pos, npalette);
pos = safecat(buffer, bufsize, pos, "]");
}
else if (npalette != 0)
pos = safecat(buffer, bufsize, pos, "+tRNS");
pos = safecat(buffer, bufsize, pos, " ");
pos = safecatn(buffer, bufsize, pos, bit_depth);
pos = safecat(buffer, bufsize, pos, " bit");
@@ -378,25 +385,32 @@ standard_name_from_id(char *buffer, size_t bufsize, size_t pos, png_uint_32 id)
static int
next_format(png_bytep colour_type, png_bytep bit_depth,
unsigned int* palette_number, int no_low_depth_gray)
unsigned int* palette_number, int low_depth_gray, int tRNS)
{
if (*bit_depth == 0)
{
*colour_type = 0;
if (no_low_depth_gray)
*bit_depth = 8;
else
if (low_depth_gray)
*bit_depth = 1;
else
*bit_depth = 8;
*palette_number = 0;
return 1;
}
if (*colour_type == 3)
if (*colour_type < 4/*no alpha channel*/)
{
/* Add multiple palettes for colour type 3. */
if (++*palette_number < PALETTE_COUNT(*bit_depth))
/* Add multiple palettes for colour type 3, one image with tRNS
* and one without for other non-alpha formats:
*/
unsigned int pn = ++*palette_number;
png_byte ct = *colour_type;
if (((ct == 0/*GRAY*/ || ct/*RGB*/ == 2) && tRNS && pn < 2) ||
(ct == 3/*PALETTE*/ && pn < PALETTE_COUNT(*bit_depth)))
return 1;
/* No: next bit depth */
*palette_number = 0;
}
@@ -1305,7 +1319,10 @@ store_current_palette(png_store *ps, int *npalette)
* operation.)
*/
if (ps->current == NULL)
{
store_log(ps, ps->pread, "no current stream for palette", 1);
return NULL;
}
/* The result may be null if there is no palette. */
*npalette = ps->current->npalette;
@@ -1959,6 +1976,7 @@ typedef struct png_modifier
/* Run tests on reading with a combination of transforms, */
unsigned int test_transform :1;
unsigned int test_tRNS :1; /* Includes tRNS images */
/* When to use the use_input_precision option, this controls the gamma
* validation code checks. If set any value that is within the transformed
@@ -1990,6 +2008,16 @@ typedef struct png_modifier
unsigned int test_gamma_expand16 :1;
unsigned int test_exhaustive :1;
/* Whether or not to run the low-bit-depth grayscale tests. This fail on
* gamma images in some cases because of gross inaccuracies in the grayscale
* gamma handling for low bit depth.
*/
unsigned int test_lbg :1;
unsigned int test_lbg_gamma_threshold :1;
unsigned int test_lbg_gamma_transform :1;
unsigned int test_lbg_gamma_sbit :1;
unsigned int test_lbg_gamma_composition :1;
unsigned int log :1; /* Log max error */
/* Buffer information, the buffer size limits the size of the chunks that can
@@ -2042,6 +2070,11 @@ modifier_init(png_modifier *pm)
pm->test_standard = 0;
pm->test_size = 0;
pm->test_transform = 0;
# ifdef PNG_WRITE_tRNS_SUPPORTED
pm->test_tRNS = 1;
# else
pm->test_tRNS = 0;
# endif
pm->use_input_precision = 0;
pm->use_input_precision_sbit = 0;
pm->use_input_precision_16to8 = 0;
@@ -2054,6 +2087,11 @@ modifier_init(png_modifier *pm)
pm->test_gamma_background = 0;
pm->test_gamma_alpha_mode = 0;
pm->test_gamma_expand16 = 0;
pm->test_lbg = 1;
pm->test_lbg_gamma_threshold = 1;
pm->test_lbg_gamma_transform = 1;
pm->test_lbg_gamma_sbit = 1;
pm->test_lbg_gamma_composition = 1;
pm->test_exhaustive = 0;
pm->log = 0;
@@ -3192,6 +3230,45 @@ init_standard_palette(png_store *ps, png_structp pp, png_infop pi, int npalette,
}
}
#ifdef PNG_WRITE_tRNS_SUPPORTED
static void
set_random_tRNS(png_structp pp, png_infop pi, PNG_CONST png_byte colour_type,
PNG_CONST int bit_depth)
{
/* To make this useful the tRNS color needs to match at least one pixel.
* Random values are fine for gray, including the 16-bit case where we know
* that the test image contains all the gray values. For RGB we need more
* method as only 65536 different RGB values are generated.
*/
png_color_16 tRNS;
const png_uint_16 mask = (png_uint_16)((1U << bit_depth)-1);
RANDOMIZE(tRNS);
if (colour_type & 2/*RGB*/)
{
if (bit_depth == 8)
{
tRNS.blue = tRNS.red ^ tRNS.green;
tRNS.red &= mask;
tRNS.green &= mask;
tRNS.blue &= mask;
}
else /* bit_depth == 16 */
{
tRNS.green = (png_uint_16)(tRNS.red * 257);
tRNS.blue = (png_uint_16)(tRNS.green * 17);
}
}
else
tRNS.gray &= mask;
png_set_tRNS(pp, pi, NULL, 0, &tRNS);
}
#endif
/* The number of passes is related to the interlace type. There was no libpng
* API to determine this prior to 1.5, so we need an inquiry function:
*/
@@ -3525,6 +3602,11 @@ make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
if (colour_type == 3) /* palette */
init_standard_palette(ps, pp, pi, 1U << bit_depth, 1/*do tRNS*/);
# ifdef PNG_WRITE_tRNS_SUPPORTED
else if (palette_number)
set_random_tRNS(pp, pi, colour_type, bit_depth);
# endif
png_write_info(pp, pi);
if (png_get_rowbytes(pp, pi) !=
@@ -3598,19 +3680,20 @@ make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
}
static void
make_transform_images(png_store *ps)
make_transform_images(png_modifier *pm)
{
png_byte colour_type = 0;
png_byte bit_depth = 0;
unsigned int palette_number = 0;
/* This is in case of errors. */
safecat(ps->test, sizeof ps->test, 0, "make standard images");
safecat(pm->this.test, sizeof pm->this.test, 0, "make standard images");
/* Use next_format to enumerate all the combinations we test, including
* generating multiple low bit depth palette images.
* generating multiple low bit depth palette images. Non-A images (palette
* and direct) are created with and without tRNS chunks.
*/
while (next_format(&colour_type, &bit_depth, &palette_number, 0))
while (next_format(&colour_type, &bit_depth, &palette_number, 1, 1))
{
int interlace_type;
@@ -3621,7 +3704,7 @@ make_transform_images(png_store *ps)
standard_name(name, sizeof name, 0, colour_type, bit_depth,
palette_number, interlace_type, 0, 0, 0);
make_transform_image(ps, colour_type, bit_depth, palette_number,
make_transform_image(&pm->this, colour_type, bit_depth, palette_number,
interlace_type, name);
}
}
@@ -4017,7 +4100,7 @@ make_error(png_store* volatile psIn, png_byte PNG_CONST colour_type,
Try
{
png_structp pp;
volatile png_structp pp;
png_infop pi;
pp = set_store_for_write(ps, &pi, name);
@@ -4287,6 +4370,7 @@ typedef struct standard_display
size_t cbRow; /* Bytes in a row of the output image */
int do_interlace; /* Do interlacing internally */
int is_transparent; /* Transparency information was present. */
int has_tRNS; /* color type GRAY or RGB with a tRNS chunk. */
int speed; /* Doing a speed test */
int use_update_info;/* Call update_info, not start_image */
struct
@@ -4619,14 +4703,14 @@ standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
case 0:
dp->transparent.red = dp->transparent.green = dp->transparent.blue =
trans_color->gray;
dp->is_transparent = 1;
dp->has_tRNS = 1;
break;
case 2:
dp->transparent.red = trans_color->red;
dp->transparent.green = trans_color->green;
dp->transparent.blue = trans_color->blue;
dp->is_transparent = 1;
dp->has_tRNS = 1;
break;
case 3:
@@ -5518,7 +5602,7 @@ image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display)
if (this->colour_type == PNG_COLOR_TYPE_GRAY)
{
if (this->bit_depth < 8)
this->bit_depth = 8;
this->bit_depth = this->sample_depth = 8;
if (this->have_tRNS)
{
@@ -5553,9 +5637,11 @@ image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display)
this->alphaf = 0;
else
this->alphaf = 1;
this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
}
else
this->alphaf = 1;
this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
}
/* The error in the alpha is zero and the sBIT value comes from the
@@ -5848,8 +5934,9 @@ transform_info_imp(transform_display *dp, png_structp pp, png_infop pi)
/* If png_set_filler is in action then fake the output color type to include
* an alpha channel where appropriate.
*/
if (dp->output_bit_depth >= 8 && (dp->output_colour_type == PNG_COLOR_TYPE_RGB ||
dp->output_colour_type == PNG_COLOR_TYPE_GRAY) && dp->this.filler)
if (dp->output_bit_depth >= 8 &&
(dp->output_colour_type == PNG_COLOR_TYPE_RGB ||
dp->output_colour_type == PNG_COLOR_TYPE_GRAY) && dp->this.filler)
dp->output_colour_type |= 4;
/* Validate the combination of colour type and bit depth that we are getting
@@ -6372,6 +6459,13 @@ image_transform_png_set_tRNS_to_alpha_set(PNG_CONST image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_tRNS_to_alpha(pp);
/* If there was a tRNS chunk that would get expanded and add an alpha
* channel is_transparent must be updated:
*/
if (that->this.has_tRNS)
that->this.is_transparent = 1;
this->next->set(this->next, that, pp, pi);
}
@@ -6430,6 +6524,7 @@ image_transform_png_set_gray_to_rgb_set(PNG_CONST image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_gray_to_rgb(pp);
/* NOTE: this doesn't result in tRNS expansion. */
this->next->set(this->next, that, pp, pi);
}
@@ -6489,6 +6584,10 @@ image_transform_png_set_expand_set(PNG_CONST image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_expand(pp);
if (that->this.has_tRNS)
that->this.is_transparent = 1;
this->next->set(this->next, that, pp, pi);
}
@@ -6539,6 +6638,7 @@ image_transform_png_set_expand_gray_1_2_4_to_8_set(
png_infop pi)
{
png_set_expand_gray_1_2_4_to_8(pp);
/* NOTE: don't expect this to expand tRNS */
this->next->set(this->next, that, pp, pi);
}
@@ -6570,6 +6670,11 @@ image_transform_png_set_expand_16_set(PNG_CONST image_transform *this,
transform_display *that, png_structp pp, png_infop pi)
{
png_set_expand_16(pp);
/* NOTE: at present libpng does SET_EXPAND as well, so tRNS is expanded. */
if (that->this.has_tRNS)
that->this.is_transparent = 1;
this->next->set(this->next, that, pp, pi);
}
@@ -6940,14 +7045,14 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this,
* When DIGITIZE is set because a pre-1.7 version of libpng is being
* tested allow a bigger slack.
*
* NOTE: this magic number was determined by experiment to be 1.25.
* There's no great merit to the value below, however it only affects
* the limit used for checking for internal calculation errors, not
* the actual limit imposed by pngvalid on the output errors.
* NOTE: this magic number was determined by experiment to be about
* 1.263. There's no great merit to the value below, however it only
* affects the limit used for checking for internal calculation errors,
* not the actual limit imposed by pngvalid on the output errors.
*/
that->pm->limit += pow(
# if DIGITIZE
1.25
1.3
# else
1.0
# endif
@@ -7111,7 +7216,8 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
const unsigned int sample_depth = that->sample_depth;
const unsigned int calc_depth = (pm->assume_16_bit_calculations ? 16 :
sample_depth);
const unsigned int gamma_depth = (sample_depth == 16 ? 16 :
const unsigned int gamma_depth = (sample_depth == 16 ?
PNG_MAX_GAMMA_8 :
(pm->assume_16_bit_calculations ? PNG_MAX_GAMMA_8 : sample_depth));
int isgray;
double r, g, b;
@@ -7125,56 +7231,73 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
* will be identical after this operation if there is only one
* transform, feel free to delete the png_error checks on this below in
* the future (this is just me trying to ensure it works!)
*
* Interval arithmetic is exact, but to implement it it must be
* possible to control the floating point implementation rounding mode.
* This cannot be done in ANSI-C, so instead I reduce the 'lo' values
* by DBL_EPSILON and increase the 'hi' values by the same.
*/
# define DD(v,d,r) (digitize(v*(1-DBL_EPSILON), d, r) * (1-DBL_EPSILON))
# define DU(v,d,r) (digitize(v*(1+DBL_EPSILON), d, r) * (1+DBL_EPSILON))
r = rlo = rhi = that->redf;
rlo -= that->rede;
rlo = digitize(rlo, calc_depth, 1/*round*/);
rlo = DD(rlo, calc_depth, 1/*round*/);
rhi += that->rede;
rhi = digitize(rhi, calc_depth, 1/*round*/);
rhi = DU(rhi, calc_depth, 1/*round*/);
g = glo = ghi = that->greenf;
glo -= that->greene;
glo = digitize(glo, calc_depth, 1/*round*/);
glo = DD(glo, calc_depth, 1/*round*/);
ghi += that->greene;
ghi = digitize(ghi, calc_depth, 1/*round*/);
ghi = DU(ghi, calc_depth, 1/*round*/);
b = blo = bhi = that->bluef;
blo -= that->bluee;
blo = digitize(blo, calc_depth, 1/*round*/);
blo = DD(blo, calc_depth, 1/*round*/);
bhi += that->greene;
bhi = digitize(bhi, calc_depth, 1/*round*/);
bhi = DU(bhi, calc_depth, 1/*round*/);
isgray = r==g && g==b;
if (data.gamma != 1)
{
PNG_CONST double power = 1/data.gamma;
PNG_CONST double abse = calc_depth == 16 ? .5/65535 : .5/255;
PNG_CONST double abse = .5/(sample_depth == 16 ? 65535 : 255);
/* 'abse' is the absolute error permitted in linear calculations. It
* is used here to capture the error permitted in the handling
* (undoing) of the gamma encoding. Once again digitization occurs
* to handle the upper and lower bounds of the values. This is
* where the real errors are introduced.
/* If a gamma calculation is done it is done using lookup tables of
* precision gamma_depth, so the already digitized value above may
* need to be further digitized here.
*/
if (gamma_depth != calc_depth)
{
rlo = DD(rlo, gamma_depth, 0/*truncate*/);
rhi = DU(rhi, gamma_depth, 0/*truncate*/);
glo = DD(glo, gamma_depth, 0/*truncate*/);
ghi = DU(ghi, gamma_depth, 0/*truncate*/);
blo = DD(blo, gamma_depth, 0/*truncate*/);
bhi = DU(bhi, gamma_depth, 0/*truncate*/);
}
/* 'abse' is the error in the gamma table calculation itself. */
r = pow(r, power);
rlo = digitize(pow(rlo, power)-abse, calc_depth, 1);
rhi = digitize(pow(rhi, power)+abse, calc_depth, 1);
rlo = DD(pow(rlo, power)-abse, calc_depth, 1);
rhi = DU(pow(rhi, power)+abse, calc_depth, 1);
g = pow(g, power);
glo = digitize(pow(glo, power)-abse, calc_depth, 1);
ghi = digitize(pow(ghi, power)+abse, calc_depth, 1);
glo = DD(pow(glo, power)-abse, calc_depth, 1);
ghi = DU(pow(ghi, power)+abse, calc_depth, 1);
b = pow(b, power);
blo = digitize(pow(blo, power)-abse, calc_depth, 1);
bhi = digitize(pow(bhi, power)+abse, calc_depth, 1);
blo = DD(pow(blo, power)-abse, calc_depth, 1);
bhi = DU(pow(bhi, power)+abse, calc_depth, 1);
}
/* Now calculate the actual gray values. Although the error in the
* coefficients depends on whether they were specified on the command
* line (in which case truncation to 15 bits happened) or not (rounding
* was used) the maxium error in an individual coefficient is always
* 1/32768, because even in the rounding case the requirement that
* 2/32768, because even in the rounding case the requirement that
* coefficients add up to 32768 can cause a larger rounding error.
*
* The only time when rounding doesn't occur in 1.5.5 and later is when
@@ -7185,19 +7308,19 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
{
PNG_CONST int do_round = data.gamma != 1 || calc_depth == 16;
PNG_CONST double ce = 1. / 32768;
PNG_CONST double ce = 2. / 32768;
graylo = digitize(rlo * (data.red_coefficient-ce) +
graylo = DD(rlo * (data.red_coefficient-ce) +
glo * (data.green_coefficient-ce) +
blo * (data.blue_coefficient-ce), gamma_depth, do_round);
if (graylo <= 0)
graylo = 0;
blo * (data.blue_coefficient-ce), calc_depth, do_round);
if (graylo > gray) /* always accept the right answer */
graylo = gray;
grayhi = digitize(rhi * (data.red_coefficient+ce) +
grayhi = DU(rhi * (data.red_coefficient+ce) +
ghi * (data.green_coefficient+ce) +
bhi * (data.blue_coefficient+ce), gamma_depth, do_round);
if (grayhi >= 1)
grayhi = 1;
bhi * (data.blue_coefficient+ce), calc_depth, do_round);
if (grayhi < gray)
grayhi = gray;
}
/* And invert the gamma. */
@@ -7205,11 +7328,25 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
{
PNG_CONST double power = data.gamma;
/* And this happens yet again, shifting the values once more. */
if (gamma_depth != sample_depth)
{
rlo = DD(rlo, gamma_depth, 0/*truncate*/);
rhi = DU(rhi, gamma_depth, 0/*truncate*/);
glo = DD(glo, gamma_depth, 0/*truncate*/);
ghi = DU(ghi, gamma_depth, 0/*truncate*/);
blo = DD(blo, gamma_depth, 0/*truncate*/);
bhi = DU(bhi, gamma_depth, 0/*truncate*/);
}
gray = pow(gray, power);
graylo = digitize(pow(graylo, power), sample_depth, 1);
grayhi = digitize(pow(grayhi, power), sample_depth, 1);
graylo = DD(pow(graylo, power), sample_depth, 1);
grayhi = DU(pow(grayhi, power), sample_depth, 1);
}
# undef DD
# undef DU
/* Now the error can be calculated.
*
* If r==g==b because there is no overall gamma correction libpng
@@ -7260,16 +7397,28 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
{
/* There is no need to do the conversions to and from linear space,
* so the calculation should be a lot more accurate. There is a
* built in 1/32768 error in the coefficients because they only have
* 15 bits and are adjusted to make sure they add up to 32768, so
* the result may have an additional error up to 1/32768. (Note
* that adding the 1/32768 here avoids needing to increase the
* global error limits to take this into account.)
* built in error in the coefficients because they only have 15 bits
* and are adjusted to make sure they add up to 32768. This
* involves a integer calculation with truncation of the form:
*
* ((int)(coefficient * 100000) * 32768)/100000
*
* This is done to the red and green coefficients (the ones
* provided to the API) then blue is calculated from them so the
* result adds up to 32768. In the worst case this can result in
* a -1 error in red and green and a +2 error in blue. Consequently
* the worst case in the calculation below is 2/32768 error.
*
* TODO: consider fixing this in libpng by rounding the calculation
* limiting the error to 1/32768.
*
* Handling this by adding 2/32768 here avoids needing to increase
* the global error limits to take this into account.)
*/
gray = r * data.red_coefficient + g * data.green_coefficient +
b * data.blue_coefficient;
err = re * data.red_coefficient + ge * data.green_coefficient +
be * data.blue_coefficient + 1./32768 + gray * 5 * DBL_EPSILON;
be * data.blue_coefficient + 2./32768 + gray * 5 * DBL_EPSILON;
}
else
@@ -7304,7 +7453,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
* previously added input quantization error at this point.
*/
gray = r * data.red_coefficient + g * data.green_coefficient +
b * data.blue_coefficient - 1./32768 - out_qe;
b * data.blue_coefficient - 2./32768 - out_qe;
if (gray <= 0)
gray = 0;
else
@@ -7314,7 +7463,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
}
grayhi = rhi * data.red_coefficient + ghi * data.green_coefficient +
bhi * data.blue_coefficient + 1./32768 + out_qe;
bhi * data.blue_coefficient + 2./32768 + out_qe;
grayhi *= (1 + 6 * DBL_EPSILON);
if (grayhi >= 1)
grayhi = 1;
@@ -7429,6 +7578,9 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this,
else
{
if (that->this.has_tRNS)
that->this.is_transparent = 1;
bit_depth = that->this.bit_depth;
expand = 1;
}
@@ -7506,14 +7658,14 @@ image_transform_png_set_background_mod(PNG_CONST image_transform *this,
/* Remove the alpha type and set the alpha (not in that order.) */
that->alphaf = 1;
that->alphae = 0;
if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
that->colour_type = PNG_COLOR_TYPE_RGB;
else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
that->colour_type = PNG_COLOR_TYPE_GRAY;
/* PNG_COLOR_TYPE_PALETTE is not changed */
}
if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
that->colour_type = PNG_COLOR_TYPE_RGB;
else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
that->colour_type = PNG_COLOR_TYPE_GRAY;
/* PNG_COLOR_TYPE_PALETTE is not changed */
this->next->mod(this->next, that, pp, display);
}
@@ -8301,7 +8453,8 @@ perform_transform_test(png_modifier *pm)
png_byte bit_depth = 0;
unsigned int palette_number = 0;
while (next_format(&colour_type, &bit_depth, &palette_number, 0))
while (next_format(&colour_type, &bit_depth, &palette_number, pm->test_lbg,
pm->test_tRNS))
{
png_uint_32 counter = 0;
size_t base_pos;
@@ -8604,7 +8757,9 @@ init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp,
vi->outlog = outlog(dp->pm, in_depth, out_depth);
if ((dp->this.colour_type & PNG_COLOR_MASK_ALPHA) != 0 ||
(dp->this.colour_type == 3 && dp->this.is_transparent))
(dp->this.colour_type == 3 && dp->this.is_transparent) ||
((dp->this.colour_type == 0 || dp->this.colour_type == 2) &&
dp->this.has_tRNS))
{
vi->do_background = dp->do_background;
@@ -8634,7 +8789,7 @@ init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp,
vi->background_blue = b;
}
}
else
else /* Do not expect any background processing */
vi->do_background = 0;
if (vi->do_background == 0)
@@ -9350,6 +9505,7 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp,
png_uint_32 y;
PNG_CONST store_palette_entry *in_palette = dp->this.palette;
PNG_CONST int in_is_transparent = dp->this.is_transparent;
int process_tRNS;
int out_npalette = -1;
int out_is_transparent = 0; /* Just refers to the palette case */
store_palette out_palette;
@@ -9365,6 +9521,7 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp,
processing = (vi.gamma_correction > 0 && !dp->threshold_test)
|| in_bd != out_bd || in_ct != out_ct || vi.do_background;
process_tRNS = dp->this.has_tRNS && vi.do_background;
/* TODO: FIX THIS: MAJOR BUG! If the transformations all happen inside
* the palette there is no way of finding out, because libpng fails to
@@ -9403,8 +9560,8 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp,
/* Handle input alpha - png_set_background will cause the output
* alpha to disappear so there is nothing to check.
*/
if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 || (in_ct == 3 &&
in_is_transparent))
if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 ||
(in_ct == 3 && in_is_transparent))
{
PNG_CONST unsigned int input_alpha = in_ct == 3 ?
dp->this.palette[in_index].alpha :
@@ -9436,6 +9593,35 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp,
}
}
else if (process_tRNS)
{
/* alpha needs to be set appropriately for this pixel, it is
* currently 1 and needs to be 0 for an input pixel which matches
* the values in tRNS.
*/
switch (in_ct)
{
case 0: /* gray */
if (sample(std, in_ct, in_bd, x, 0, 0, 0) ==
dp->this.transparent.red)
alpha = 0;
break;
case 2: /* RGB */
if (sample(std, in_ct, in_bd, x, 0, 0, 0) ==
dp->this.transparent.red &&
sample(std, in_ct, in_bd, x, 1, 0, 0) ==
dp->this.transparent.green &&
sample(std, in_ct, in_bd, x, 2, 0, 0) ==
dp->this.transparent.blue)
alpha = 0;
break;
default:
break;
}
}
/* Handle grayscale or RGB components. */
if ((in_ct & PNG_COLOR_MASK_COLOR) == 0) /* grayscale */
(void)gamma_component_validate("gray", &vi,
@@ -9545,7 +9731,7 @@ gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn,
modification_reset(d.pm->modifications);
/* Get a png_struct for writing the image. */
/* Get a png_struct for reading the image. */
pp = set_modifier_for_read(d.pm, &pi, d.this.id, name);
standard_palette_init(&d.this);
@@ -9684,9 +9870,13 @@ perform_gamma_threshold_tests(png_modifier *pm)
/* Don't test more than one instance of each palette - it's pointless, in
* fact this test is somewhat excessive since libpng doesn't make this
* decision based on colour type or bit depth!
*
* CHANGED: now test two palettes and, as a side effect, images with and
* without tRNS.
*/
while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/))
if (palette_number == 0)
while (next_format(&colour_type, &bit_depth, &palette_number,
pm->test_lbg_gamma_threshold, pm->test_tRNS))
if (palette_number < 2)
{
double test_gamma = 1.0;
while (test_gamma >= .4)
@@ -9746,7 +9936,8 @@ static void perform_gamma_transform_tests(png_modifier *pm)
png_byte bit_depth = 0;
unsigned int palette_number = 0;
while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/))
while (next_format(&colour_type, &bit_depth, &palette_number,
pm->test_lbg_gamma_transform, pm->test_tRNS))
{
unsigned int i, j;
@@ -9776,7 +9967,8 @@ static void perform_gamma_sbit_tests(png_modifier *pm)
png_byte colour_type = 0, bit_depth = 0;
unsigned int npalette = 0;
while (next_format(&colour_type, &bit_depth, &npalette, 1/*gamma*/))
while (next_format(&colour_type, &bit_depth, &npalette,
pm->test_lbg_gamma_sbit, pm->test_tRNS))
if ((colour_type & PNG_COLOR_MASK_ALPHA) == 0 &&
((colour_type == 3 && sbit < 8) ||
(colour_type != 3 && sbit < bit_depth)))
@@ -9967,8 +10159,17 @@ static void gamma_composition_test(png_modifier *pm,
}
background.index = 193; /* rgb(193,193,193) to detect errors */
if (!(colour_type & PNG_COLOR_MASK_COLOR))
{
/* Because, currently, png_set_background is always called with
* 'need_expand' false in this case and because the gamma test itself
* doesn't cause an expand to 8-bit for lower bit depths the colour must
* be reduced to the correct range.
*/
if (bit_depth < 8)
background.gray &= (png_uint_16)((1U << bit_depth)-1);
/* Grayscale input, we do not convert to RGB (TBD), so we must set the
* background to gray - else libpng seems to fail.
*/
@@ -10017,9 +10218,18 @@ perform_gamma_composition_tests(png_modifier *pm, int do_background,
/* Skip the non-alpha cases - there is no setting of a transparency colour at
* present.
*
* TODO: incorrect; the palette case sets tRNS and, now RGB and gray do,
* however the palette case fails miserably so is commented out below.
*/
while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/))
if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0)
while (next_format(&colour_type, &bit_depth, &palette_number,
pm->test_lbg_gamma_composition, pm->test_tRNS))
if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0
#if 0 /* TODO: FIXME */
/*TODO: FIXME: this should work */
|| colour_type == 3
#endif
|| (colour_type != 3 && palette_number != 0))
{
unsigned int i, j;
@@ -10751,6 +10961,18 @@ int main(int argc, char **argv)
pm.ngammas = ARRAY_SIZE(gammas);
pm.ngamma_tests = 0; /* default to off */
/* Low bit depth gray images don't do well in the gamma tests, until
* this is fixed turn them off for some gamma cases:
*/
# ifdef PNG_WRITE_tRNS_SUPPORTED
pm.test_tRNS = 1;
# endif
pm.test_lbg = 0;
pm.test_lbg_gamma_threshold = 1;
pm.test_lbg_gamma_transform = 0/*PNG_LIBPNG_VER >= 10700*/;
pm.test_lbg_gamma_sbit = 1;
pm.test_lbg_gamma_composition = 0;
/* And the test encodings */
pm.encodings = test_encodings;
pm.nencodings = ARRAY_SIZE(test_encodings);
@@ -10863,7 +11085,7 @@ int main(int argc, char **argv)
pm.test_gamma_transform = 1;
pm.test_gamma_sbit = 1;
pm.test_gamma_scale16 = 1;
pm.test_gamma_background = 1;
pm.test_gamma_background = 1; /* composition */
pm.test_gamma_alpha_mode = 1;
}
@@ -10912,6 +11134,24 @@ int main(int argc, char **argv)
else if (strcmp(*argv, "--noexpand16") == 0)
pm.test_gamma_expand16 = 0;
else if (strcmp(*argv, "--low-depth-gray") == 0)
pm.test_lbg = pm.test_lbg_gamma_threshold =
pm.test_lbg_gamma_transform = pm.test_lbg_gamma_sbit =
pm.test_lbg_gamma_composition = 1;
else if (strcmp(*argv, "--nolow-depth-gray") == 0)
pm.test_lbg = pm.test_lbg_gamma_threshold =
pm.test_lbg_gamma_transform = pm.test_lbg_gamma_sbit =
pm.test_lbg_gamma_composition = 0;
# ifdef PNG_WRITE_tRNS_SUPPORTED
else if (strcmp(*argv, "--tRNS") == 0)
pm.test_tRNS = 1;
# endif
else if (strcmp(*argv, "--notRNS") == 0)
pm.test_tRNS = 0;
else if (strcmp(*argv, "--more-gammas") == 0)
pm.ngamma_tests = 3U;
@@ -11102,7 +11342,7 @@ int main(int argc, char **argv)
Try
{
/* Make useful base images */
make_transform_images(&pm.this);
make_transform_images(&pm);
/* Perform the standard and gamma tests. */
if (pm.test_standard)

867
contrib/tools/genpng.c Normal file
View File

@@ -0,0 +1,867 @@
/*- genpng
*
* 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.
*
* Generate a PNG with an alpha channel, correctly.
*
* This is a test case generator; the resultant PNG files are only of interest
* to those of us who care about whether the edges of circles are green, red,
* or yellow.
*
* The program generates an RGB+Alpha PNG of a given size containing the given
* shapes on a transparent background:
*
* genpng width height { shape }
* shape ::= color width shape x1 y1 x2 y2
*
* 'color' is:
*
* black white red green yellow blue brown purple pink orange gray cyan
*
* The point is to have colors that are linguistically meaningful plus that old
* bugbear of the department store dress murders, Cyan, the only color we argue
* about.
*
* 'shape' is:
*
* circle: an ellipse
* square: a rectangle
* line: a straight line
*
* Each shape is followed by four numbers, these are two points in the output
* coordinate space (as real numbers) which describe the circle, square, or
* line. The shape is filled if it is preceded by 'filled' (not valid for
* 'line') or is drawn with a line, in which case the width of the line must
* precede the shape.
*
* The whole set of information can be repeated as many times as desired:
*
* shape ::= color width shape x1 y1 x2 y2
*
* color ::= black|white|red|green|yellow|blue
* color ::= brown|purple|pink|orange|gray|cyan
* width ::= filled
* width ::= <number>
* shape ::= circle|square|line
* x1 ::= <number>
* x2 ::= <number>
* y1 ::= <number>
* y2 ::= <number>
*
* The output PNG is generated by down-sampling a 4x supersampled image using
* a bi-cubic filter. The bi-cubic has a 2 (output) pixel width, so an 8x8
* array of super-sampled points contribute to each output pixel. The value of
* a super-sampled point is found using an unfiltered, aliased, infinite
* precision image: Each shape from the last to the first is checked to see if
* the point is in the drawn area and, if it is, the color of the point is the
* color of the shape and the alpha is 1, if not the previous shape is checked.
*
* This is an aliased algorithm because no filtering is done; a point is either
* inside or outside each shape and 'close' points do not contribute to the
* sample. The down-sampling is relied on to correct the error of not using
* a filter.
*
* The line end-caps are 'flat'; they go through the points. The square line
* joins are mitres; the outside of the lines are continued to the point of
* intersection.
*/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.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_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
static const struct color
{
const char *name;
double red;
double green;
double blue;
} colors[] =
/* color ::= black|white|red|green|yellow|blue
* color ::= brown|purple|pink|orange|gray|cyan
*/
{
{ "black", 0, 0, 0 },
{ "white", 1, 1, 1 },
{ "red", 1, 0, 0 },
{ "green", 0, 1, 0 },
{ "yellow", 1, 1, 0 },
{ "blue", 0, 0, 1 },
{ "brown", .5, .125, 0 },
{ "purple", 1, 0, 1 },
{ "pink", 1, .5, .5 },
{ "orange", 1, .5, 0 },
{ "gray", 0, .5, .5 },
{ "cyan", 0, 1, 1 }
};
#define color_count ((sizeof colors)/(sizeof colors[0]))
static const struct color *
color_of(const char *arg)
{
int icolor = color_count;
while (--icolor >= 0)
{
if (strcmp(colors[icolor].name, arg) == 0)
return colors+icolor;
}
fprintf(stderr, "genpng: invalid color %s\n", arg);
exit(1);
}
static double
width_of(const char *arg)
{
if (strcmp(arg, "filled") == 0)
return 0;
else
{
char *ep = NULL;
double w = strtod(arg, &ep);
if (ep != NULL && *ep == 0 && w > 0)
return w;
}
fprintf(stderr, "genpng: invalid line width %s\n", arg);
exit(1);
}
static double
coordinate_of(const char *arg)
{
char *ep = NULL;
double w = strtod(arg, &ep);
if (ep != NULL && *ep == 0)
return w;
fprintf(stderr, "genpng: invalid coordinate value %s\n", arg);
exit(1);
}
struct arg; /* forward declaration */
typedef int (*shape_fn_ptr)(const struct arg *arg, double x, double y);
/* A function to determine if (x,y) is inside the shape.
*
* There are two implementations:
*
* inside_fn: returns true if the point is inside
* check_fn: returns;
* -1: the point is outside the shape by more than the filter width (2)
* 0: the point may be inside the shape
* +1: the point is inside the shape by more than the filter width
*/
#define OUTSIDE (-1)
#define INSIDE (1)
struct arg
{
const struct color *color;
shape_fn_ptr inside_fn;
shape_fn_ptr check_fn;
double width; /* line width, 0 for 'filled' */
double x1, y1, x2, y2;
};
/* IMPLEMENTATION NOTE:
*
* We want the contribution of each shape to the sample corresponding to each
* pixel. This could be obtained by super sampling the image to infinite
* dimensions, finding each point within the shape and assigning that a value
* '1' while leaving every point outside the shape with value '0' then
* downsampling to the image size with sinc; computationally very expensive.
*
* Approximations are as follows:
*
* 1) If the pixel coordinate is within the shape assume the sample has the
* shape color and is opaque, else assume there is no contribution from
* the shape.
*
* This is the equivalent of aliased rendering or resampling an image with
* a block filter. The maximum error in the calculated alpha (which will
* always be 0 or 1) is 0.5.
*
* 2) If the shape is within a square of size 1x1 centered on the pixel assume
* that the shape obscures an amount of the pixel equal to its area within
* that square.
*
* This is the equivalent of 'pixel coverage' alpha calculation or resampling
* an image with a bi-linear filter. The maximum error is over 0.2, but the
* results are often acceptable.
*
* This can be approximated by applying (1) to a super-sampled image then
* downsampling with a bi-linear filter. The error in the super-sampled
* image is 0.5 per sample, but the resampling reduces this.
*
* 3) Use a better filter with a super-sampled image; in the limit this is the
* sinc() approach.
*
* 4) Do the geometric calculation; a bivariate definite integral across the
* shape, unfortunately this means evaluating Si(x), the integral of sinc(x),
* which is still a lot of math.
*
* This code uses approach (3) with a bi-cubic filter and 8x super-sampling
* and method (1) for the super-samples. This means that the sample is either
* 0 or 1, depending on whether the sub-pixel is within or outside the shape.
* The bi-cubic weights are also fixed and the 16 required weights are
* pre-computed here (note that the 'scale' setting will need to be changed if
* 'super' is increased).
*
* The code also calculates a sum to the edge of the filter. This is not
* currently used by could be used to optimize the calculation.
*/
#if 0 /* bc code */
scale=10
super=8
define bicubic(x) {
if (x <= 1) return (1.5*x - 2.5)*x*x + 1;
if (x < 2) return (((2.5 - 0.5*x)*x - 4)*x + 2);
return 0;
}
define sum(x) {
auto s;
s = 0;
while (x < 2*super) {
s = s + bicubic(x/super);
x = x + 1;
}
return s;
}
define results(x) {
auto b, s;
b = bicubic(x/super);
s = sum(x);
print " /*", x, "*/ { ", b, ", ", s, " }";
return 1;
}
x=0
while (x<2*super) {
x = x + results(x)
if (x < 2*super) print ","
print "\n"
}
quit
#endif
#define BICUBIC1(x) /* |x| <= 1 */ ((1.5*(x)* - 2.5)*(x)*(x) + 1)
#define BICUBIC2(x) /* 1 < |x| < 2 */ (((2.5 - 0.5*(x))*(x) - 4)*(x) + 2)
#define FILTER_WEIGHT 9 /* Twice the first sum below */
#define FILTER_WIDTH 2 /* Actually half the width; -2..+2 */
#define FILTER_STEPS 8 /* steps per filter unit */
static const double
bicubic[16][2] =
{
/* These numbers are exact; the weight for the filter is 1/9, but this
* would make the numbers inexact, so it is not included here.
*/
/* bicubic sum */
/* 0*/ { 1.0000000000, 4.5000000000 },
/* 1*/ { .9638671875, 3.5000000000 },
/* 2*/ { .8671875000, 2.5361328125 },
/* 3*/ { .7275390625, 1.6689453125 },
/* 4*/ { .5625000000, .9414062500 },
/* 5*/ { .3896484375, .3789062500 },
/* 6*/ { .2265625000, -.0107421875 },
/* 7*/ { .0908203125, -.2373046875 },
/* 8*/ { 0, -.3281250000 },
/* 9*/ { -.0478515625, -.3281250000 },
/*10*/ { -.0703125000, -.2802734375 },
/*11*/ { -.0732421875, -.2099609375 },
/*12*/ { -.0625000000, -.1367187500 },
/*13*/ { -.0439453125, -.0742187500 },
/*14*/ { -.0234375000, -.0302734375 },
/*15*/ { -.0068359375, -.0068359375 }
};
static double
alpha_calc(const struct arg *arg, double x, double y)
{
/* For [x-2..x+2],[y-2,y+2] calculate the weighted bicubic given a function
* which tells us whether a point is inside or outside the shape. First
* check if we need to do this at all:
*/
switch (arg->check_fn(arg, x, y))
{
case OUTSIDE:
return 0; /* all samples outside the shape */
case INSIDE:
return 1; /* all samples inside the shape */
default:
{
int dy;
double alpha = 0;
# define FILTER_D (FILTER_WIDTH*FILTER_STEPS-1)
for (dy=-FILTER_D; dy<=FILTER_D; ++dy)
{
double wy = bicubic[abs(dy)][0];
if (wy != 0)
{
double alphay = 0;
int dx;
for (dx=-FILTER_D; dx<=FILTER_D; ++dx)
{
double wx = bicubic[abs(dx)][0];
if (wx != 0 && arg->inside_fn(arg, x+dx/16, y+dy/16))
alphay += wx;
}
alpha += wy * alphay;
}
}
/* This needs to be weighted for each dimension: */
return alpha / (FILTER_WEIGHT*FILTER_WEIGHT);
}
}
}
/* These are the shape functions. */
/* "square",
* { inside_square_filled, check_square_filled },
* { inside_square, check_square }
*/
static int
square_check(double x, double y, double x1, double y1, double x2, double y2)
/* Is x,y inside the square (x1,y1)..(x2,y2)? */
{
/* Do a modified Cohen-Sutherland on one point, bit patterns that indicate
* 'outside' are:
*
* x<x1 | x<y1 | x<x2 | x<y2
* 0 x 0 x To the right
* 1 x 1 x To the left
* x 0 x 0 Below
* x 1 x 1 Above
*
* So 'inside' is (x<x1) != (x<x2) && (y<y1) != (y<y2);
*/
return ((x<x1) ^ (x<x2)) & ((y<y1) ^ (y<y2));
}
static int
inside_square_filled(const struct arg *arg, double x, double y)
{
return square_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2);
}
static int
square_check_line(const struct arg *arg, double x, double y, double w)
/* Check for a point being inside the boundaries implied by the given arg
* and assuming a width 2*w each side of the boundaries. This returns the
* 'check' INSIDE/OUTSIDE/0 result but note the semantics:
*
* +--------------+
* | | OUTSIDE
* | INSIDE |
* | |
* +--------------+
*
* And '0' means within the line boundaries.
*/
{
double cx = (arg->x1+arg->x2)/2;
double wx = fabs(arg->x1-arg->x2)/2;
double cy = (arg->y1+arg->y2)/2;
double wy = fabs(arg->y1-arg->y2)/2;
if (square_check(x, y, cx-wx-w, cy-wy-w, cx+wx+w, cy+wy+w))
{
/* Inside, but maybe too far; check for the redundant case where
* the lines overlap:
*/
wx -= w;
wy -= w;
if (wx > 0 && wy > 0 && square_check(x, y, cx-wx, cy-wy, cx+wx, cy+wy))
return INSIDE; /* between (inside) the boundary lines. */
return 0; /* inside the lines themselves. */
}
return OUTSIDE; /* outside the boundary lines. */
}
static int
check_square_filled(const struct arg *arg, double x, double y)
{
/* The filter extends +/-FILTER_WIDTH each side of each output point, so
* the check has to expand and contract the square by that amount; '0'
* means close enough to the edge of the square that the bicubic filter has
* to be run, OUTSIDE means alpha==0, INSIDE means alpha==1.
*/
return square_check_line(arg, x, y, FILTER_WIDTH);
}
static int
inside_square(const struct arg *arg, double x, double y)
{
/* Return true if within the drawn lines, else false, no need to distinguish
* INSIDE vs OUTSIDE here:
*/
return square_check_line(arg, x, y, arg->width/2) == 0;
}
static int
check_square(const struct arg *arg, double x, double y)
{
/* So for this function a result of 'INSIDE' means inside the actual lines.
*/
double w = arg->width/2;
if (square_check_line(arg, x, y, w+FILTER_WIDTH) == 0)
{
/* Somewhere close to the boundary lines. If far enough inside one of
* them then we can return INSIDE:
*/
w -= FILTER_WIDTH;
if (w > 0 && square_check_line(arg, x, y, w) == 0)
return INSIDE;
/* Point is somewhere in the filter region: */
return 0;
}
else /* Inside or outside the square by more than w+FILTER_WIDTH. */
return OUTSIDE;
}
/* "circle",
* { inside_circle_filled, check_circle_filled },
* { inside_circle, check_circle }
*
* The functions here are analoguous to the square ones; however, they check
* the corresponding ellipse as opposed to the rectangle.
*/
static int
circle_check(double x, double y, double x1, double y1, double x2, double y2)
{
if (square_check(x, y, x1, y1, x2, y2))
{
/* Inside the square, so maybe inside the circle too: */
const double cx = (x1 + x2)/2;
const double cy = (y1 + y2)/2;
const double dx = x1 - x2;
const double dy = y1 - y2;
x = (x - cx)/dx;
y = (y - cy)/dy;
/* It is outside if the distance from the center is more than half the
* diameter:
*/
return x*x+y*y < .25;
}
return 0; /* outside */
}
static int
inside_circle_filled(const struct arg *arg, double x, double y)
{
return circle_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2);
}
static int
circle_check_line(const struct arg *arg, double x, double y, double w)
/* Check for a point being inside the boundaries implied by the given arg
* and assuming a width 2*w each side of the boundaries. This function has
* the same semantic as square_check_line but tests the circle.
*/
{
double cx = (arg->x1+arg->x2)/2;
double wx = fabs(arg->x1-arg->x2)/2;
double cy = (arg->y1+arg->y2)/2;
double wy = fabs(arg->y1-arg->y2)/2;
if (circle_check(x, y, cx-wx-w, cy-wy-w, cx+wx+w, cy+wy+w))
{
/* Inside, but maybe too far; check for the redundant case where
* the lines overlap:
*/
wx -= w;
wy -= w;
if (wx > 0 && wy > 0 && circle_check(x, y, cx-wx, cy-wy, cx+wx, cy+wy))
return INSIDE; /* between (inside) the boundary lines. */
return 0; /* inside the lines themselves. */
}
return OUTSIDE; /* outside the boundary lines. */
}
static int
check_circle_filled(const struct arg *arg, double x, double y)
{
return circle_check_line(arg, x, y, FILTER_WIDTH);
}
static int
inside_circle(const struct arg *arg, double x, double y)
{
return circle_check_line(arg, x, y, arg->width/2) == 0;
}
static int
check_circle(const struct arg *arg, double x, double y)
{
/* Exactly as the 'square' code. */
double w = arg->width/2;
if (circle_check_line(arg, x, y, w+FILTER_WIDTH) == 0)
{
w -= FILTER_WIDTH;
if (w > 0 && circle_check_line(arg, x, y, w) == 0)
return INSIDE;
/* Point is somewhere in the filter region: */
return 0;
}
else /* Inside or outside the square by more than w+FILTER_WIDTH. */
return OUTSIDE;
}
/* "line",
* { NULL, NULL }, There is no 'filled' line.
* { inside_line, check_line }
*/
static int
line_check(double x, double y, double x1, double y1, double x2, double y2,
double w, double expand)
{
/* Shift all the points to (arg->x1, arg->y1) */
double lx = x2 - x1;
double ly = y2 - y1;
double len2 = lx*lx + ly*ly;
double cross, dot;
x -= x1;
y -= y1;
/* The dot product is the distance down the line, the cross product is
* the distance away from the line:
*
* distance = |cross| / sqrt(len2)
*/
cross = x * ly - y * lx;
/* If 'distance' is more than w the point is definitely outside the line:
*
* distance >= w
* |cross| >= w * sqrt(len2)
* cross^2 >= w^2 * len2:
*/
if (cross*cross >= (w+expand)*(w+expand)*len2)
return 0; /* outside */
/* Now find the distance *along* the line; this comes from the dot product
* lx.x+ly.y. The actual distance (in pixels) is:
*
* distance = dot / sqrt(len2)
*/
dot = lx * x + ly * y;
/* The test for 'outside' is:
*
* distance < 0 || distance > sqrt(len2)
* -> dot / sqrt(len2) > sqrt(len2)
* -> dot > len2
*
* But 'expand' is used for the filter width and needs to be handled too:
*/
return dot > -expand && dot < len2+expand;
}
static int
inside_line(const struct arg *arg, double x, double y)
{
return line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2, 0);
}
static int
check_line(const struct arg *arg, double x, double y)
{
/* The end caps of the line must be checked too; it's not enough just to
* widen the line by FILTER_WIDTH; 'expand' exists for this purpose:
*/
if (line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2,
FILTER_WIDTH))
{
/* Inside the line+filter; far enough inside that the filter isn't
* required?
*/
if (arg->width > 2*FILTER_WIDTH &&
line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2,
-FILTER_WIDTH))
return INSIDE;
return 0;
}
return OUTSIDE;
}
static const struct
{
const char *name;
shape_fn_ptr function[2/*fill,line*/][2];
# define FN_INSIDE 0
# define FN_CHECK 1
} shape_defs[] =
{
{ "square",
{ { inside_square_filled, check_square_filled },
{ inside_square, check_square } }
},
{ "circle",
{ { inside_circle_filled, check_circle_filled },
{ inside_circle, check_circle } }
},
{ "line",
{ { NULL, NULL },
{ inside_line, check_line } }
}
};
#define shape_count ((sizeof shape_defs)/(sizeof shape_defs[0]))
static shape_fn_ptr
shape_of(const char *arg, double width, int f)
{
unsigned int i;
for (i=0; i<shape_count; ++i) if (strcmp(shape_defs[i].name, arg) == 0)
{
shape_fn_ptr fn = shape_defs[i].function[width != 0][f];
if (fn != NULL)
return fn;
fprintf(stderr, "genpng: %s %s not supported\n",
width == 0 ? "filled" : "unfilled", arg);
exit(1);
}
fprintf(stderr, "genpng: %s: not a valid shape name\n", arg);
exit(1);
}
static void
parse_arg(struct arg *arg, const char **argv/*7 arguments*/)
{
/* shape ::= color width shape x1 y1 x2 y2 */
arg->color = color_of(argv[0]);
arg->width = width_of(argv[1]);
arg->inside_fn = shape_of(argv[2], arg->width, FN_INSIDE);
arg->check_fn = shape_of(argv[2], arg->width, FN_CHECK);
arg->x1 = coordinate_of(argv[3]);
arg->y1 = coordinate_of(argv[4]);
arg->x2 = coordinate_of(argv[5]);
arg->y2 = coordinate_of(argv[6]);
}
static png_uint_32
read_wh(const char *name, const char *str)
/* read a PNG width or height */
{
char *ep = NULL;
unsigned long ul = strtoul(str, &ep, 10);
if (ep != NULL && *ep == 0 && ul > 0 && ul <= 0x7fffffff)
return (png_uint_32)/*SAFE*/ul;
fprintf(stderr, "genpng: %s: invalid number %s\n", name, str);
exit(1);
}
static void
pixel(png_uint_16p p, struct arg *args, int nargs, double x, double y)
{
/* Fill in the pixel by checking each shape (args[nargs]) for effects on
* the corresponding sample:
*/
double r=0, g=0, b=0, a=0;
while (--nargs >= 0 && a != 1)
{
/* NOTE: alpha_calc can return a value outside the range 0..1 with the
* bicubic filter.
*/
const double alpha = alpha_calc(args+nargs, x, y) * (1-a);
r += alpha * args[nargs].color->red;
g += alpha * args[nargs].color->green;
b += alpha * args[nargs].color->blue;
a += alpha;
}
/* 'a' may be negative or greater than 1; if it is, negative clamp the
* pixel to 0 if >1 clamp r/g/b:
*/
if (a > 0)
{
if (a > 1)
{
if (r > 1) r = 1;
if (g > 1) g = 1;
if (b > 1) b = 1;
a = 1;
}
/* And fill in the pixel: */
p[0] = (png_uint_16)/*SAFE*/round(r * 65535);
p[1] = (png_uint_16)/*SAFE*/round(g * 65535);
p[2] = (png_uint_16)/*SAFE*/round(b * 65535);
p[3] = (png_uint_16)/*SAFE*/round(a * 65535);
}
else
p[3] = p[2] = p[1] = p[0] = 0;
}
int
main(int argc, const char **argv)
{
int convert_to_8bit = 0;
/* There is one option: --8bit: */
if (argc > 1 && strcmp(argv[1], "--8bit") == 0)
--argc, ++argv, convert_to_8bit = 1;
if (argc >= 3)
{
png_uint_16p buffer;
int nshapes;
png_image image;
# define max_shapes 256
struct arg arg_list[max_shapes];
/* The libpng Simplified API write code requires a fully initialized
* structure.
*/
memset(&image, 0, sizeof image);
image.version = PNG_IMAGE_VERSION;
image.opaque = NULL;
image.width = read_wh("width", argv[1]);
image.height = read_wh("height", argv[2]);
image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
image.flags = 0;
image.colormap_entries = 0;
/* Check the remainder of the arguments */
for (nshapes=0; 3+7*(nshapes+1) <= argc && nshapes < max_shapes;
++nshapes)
parse_arg(arg_list+nshapes, argv+3+7*nshapes);
if (3+7*nshapes != argc)
{
fprintf(stderr, "genpng: %s: too many arguments\n", argv[3+7*nshapes]);
return 1;
}
/* Create the buffer: */
buffer = malloc(PNG_IMAGE_SIZE(image));
if (buffer != NULL)
{
png_uint_32 y;
/* Write each row... */
for (y=0; y<image.height; ++y)
{
png_uint_32 x;
/* Each pixel in each row: */
for (x=0; x<image.width; ++x)
pixel(buffer + 4*(x + y*image.width), arg_list, nshapes, x, y);
}
/* Write the result (to stdout) */
if (png_image_write_to_stdio(&image, stdout, convert_to_8bit,
buffer, 0/*row_stride*/, NULL/*colormap*/))
{
free(buffer);
return 0; /* success */
}
else
fprintf(stderr, "genpng: write stdout: %s\n", image.message);
free(buffer);
}
else
fprintf(stderr, "genpng: out of memory: %lu bytes\n",
(unsigned long)PNG_IMAGE_SIZE(image));
}
else
{
/* Wrong number of arguments */
fprintf(stderr, "genpng: usage: genpng [--8bit] width height {shape}\n"
" Generate a transparent PNG in RGBA (truecolor+alpha) format\n"
" containing the given shape or shapes. Shapes are defined:\n"
"\n"
" shape ::= color width shape x1 y1 x2 y2\n"
" color ::= black|white|red|green|yellow|blue\n"
" color ::= brown|purple|pink|orange|gray|cyan\n"
" width ::= filled|<number>\n"
" shape ::= circle|square|line\n"
" x1,x2 ::= <number>\n"
" y1,y2 ::= <number>\n"
"\n"
" Numbers are floating point numbers describing points relative to\n"
" the top left of the output PNG as pixel coordinates. The 'width'\n"
" parameter is either the width of the line (in output pixels) used\n"
" to draw the shape or 'filled' to indicate that the shape should\n"
" be filled with the color.\n"
"\n"
" Colors are interpreted loosely to give access to the eight full\n"
" intensity RGB values:\n"
"\n"
" black, red, green, blue, yellow, cyan, purple, white,\n"
"\n"
" Cyan is full intensity blue+green; RGB(0,1,1), plus the following\n"
" lower intensity values:\n"
"\n"
" brown: red+orange: RGB(0.5, 0.125, 0) (dark red+orange)\n"
" pink: red+white: RGB(1.0, 0.5, 0.5)\n"
" orange: red+yellow: RGB(1.0, 0.5, 0)\n"
" gray: black+white: RGB(0.5, 0.5, 0.5)\n"
"\n"
" The RGB values are selected to make detection of aliasing errors\n"
" easy. The names are selected to make the description of errors\n"
" easy.\n"
"\n"
" The PNG is written to stdout, if --8bit is given a 32bpp RGBA sRGB\n"
" file is produced, otherwise a 64bpp RGBA linear encoded file is\n"
" written.\n");
}
return 1;
}
#endif /* SIMPLIFIED_WRITE && STDIO */

View File

@@ -1,8 +1,8 @@
/* png-fix-itxt version 1.0.0
*
* Copyright 2013 Glenn Randers-Pehrson
* Last changed in libpng 1.6.3 [July 18, 2013]
* Copyright 2015 Glenn Randers-Pehrson
* Last changed in libpng 1.6.18 [July 23, 2015]
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
@@ -34,8 +34,10 @@
#define MAX_LENGTH 500000
#define GETBREAK ((unsigned char)(inchar=getchar())); if (inchar == EOF) break
/* Read one character (inchar), also return octet (c), break if EOF */
#define GETBREAK inchar=getchar(); \
c=(inchar & 0xffU);\
if (inchar != c) break
int
main(void)
{
@@ -48,25 +50,25 @@ main(void)
/* Skip 8-byte signature */
for (i=8; i; i--)
{
c=GETBREAK;
GETBREAK;
putchar(c);
}
if (inchar != EOF)
if (inchar == c) /* !EOF */
for (;;)
{
/* Read the length */
unsigned long length; /* must be 32 bits! */
c=GETBREAK; buf[0] = c; length = c; length <<= 8;
c=GETBREAK; buf[1] = c; length += c; length <<= 8;
c=GETBREAK; buf[2] = c; length += c; length <<= 8;
c=GETBREAK; buf[3] = c; length += c;
GETBREAK; buf[0] = c; length = c; length <<= 8;
GETBREAK; buf[1] = c; length += c; length <<= 8;
GETBREAK; buf[2] = c; length += c; length <<= 8;
GETBREAK; buf[3] = c; length += c;
/* Read the chunkname */
c=GETBREAK; buf[4] = c;
c=GETBREAK; buf[5] = c;
c=GETBREAK; buf[6] = c;
c=GETBREAK; buf[7] = c;
GETBREAK; buf[4] = c;
GETBREAK; buf[5] = c;
GETBREAK; buf[6] = c;
GETBREAK; buf[7] = c;
/* The iTXt chunk type expressed as integers is (105, 84, 88, 116) */
@@ -81,19 +83,22 @@ for (;;)
/* Copy the data bytes */
for (i=8; i < length + 12; i++)
{
c=GETBREAK; buf[i] = c;
GETBREAK; buf[i] = c;
}
if (inchar != c) /* EOF */
break;
/* Calculate the CRC */
crc = crc32(crc, buf+4, (uInt)length+4);
for (;;)
{
/* Check the CRC */
if (((crc >> 24) & 0xff) == buf[length+8] &&
((crc >> 16) & 0xff) == buf[length+9] &&
((crc >> 8) & 0xff) == buf[length+10] &&
((crc ) & 0xff) == buf[length+11])
if (((crc >> 24) & 0xffU) == buf[length+8] &&
((crc >> 16) & 0xffU) == buf[length+9] &&
((crc >> 8) & 0xffU) == buf[length+10] &&
((crc ) & 0xffU) == buf[length+11])
break;
length++;
@@ -101,18 +106,21 @@ for (;;)
if (length >= MAX_LENGTH-12)
break;
c=GETBREAK;
buf[length+11]=c;
GETBREAK;
buf[length+11] = c;
/* Update the CRC */
crc = crc32(crc, buf+7+length, 1);
}
if (inchar != c) /* EOF */
break;
/* Update length bytes */
buf[0] = (unsigned char)((length << 24) & 0xff);
buf[1] = (unsigned char)((length << 16) & 0xff);
buf[2] = (unsigned char)((length << 8) & 0xff);
buf[3] = (unsigned char)((length ) & 0xff);
buf[0] = (unsigned char)((length >> 24) & 0xffU);
buf[1] = (unsigned char)((length >> 16) & 0xffU);
buf[2] = (unsigned char)((length >> 8) & 0xffU);
buf[3] = (unsigned char)((length ) & 0xffU);
/* Write the fixed iTXt chunk (length, name, data, crc) */
for (i=0; i<length+12; i++)
@@ -121,6 +129,9 @@ for (;;)
else
{
if (inchar != c) /* EOF */
break;
/* Copy bytes that were already read (length and chunk name) */
for (i=0; i<8; i++)
putchar(buf[i]);
@@ -128,11 +139,11 @@ for (;;)
/* Copy data bytes and CRC */
for (i=8; i< length+12; i++)
{
c=GETBREAK;
GETBREAK;
putchar(c);
}
if (inchar == EOF)
if (inchar != c) /* EOF */
{
break;
}
@@ -142,7 +153,7 @@ for (;;)
break;
}
if (inchar == EOF)
if (inchar != c) /* EOF */
break;
if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)

View File

@@ -2,7 +2,7 @@
*
* Copyright (c) 2014-2015 John Cunningham Bowler
*
* Last changed in libpng 1.6.17 [March 26, 2015]
* Last changed in libpng 1.6.18 [July 23, 2015]
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
@@ -71,8 +71,8 @@
* with older builds.
*/
#if ZLIB_VERNUM < 0x1260
# define PNGZ_MSG_CAST(s) png_constcast(char*,s)
# define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b)
# define PNGZ_MSG_CAST(s) constcast(char*,s)
# define PNGZ_INPUT_CAST(b) constcast(png_bytep,b)
#else
# define PNGZ_MSG_CAST(s) (s)
# define PNGZ_INPUT_CAST(b) (b)
@@ -86,17 +86,17 @@
/* Copied from pngpriv.h */
#ifdef __cplusplus
# define png_voidcast(type, value) static_cast<type>(value)
# define png_constcast(type, value) const_cast<type>(value)
# define png_aligncast(type, value) \
# define voidcast(type, value) static_cast<type>(value)
# define constcast(type, value) const_cast<type>(value)
# define aligncast(type, value) \
static_cast<type>(static_cast<void*>(value))
# define png_aligncastconst(type, value) \
# define aligncastconst(type, value) \
static_cast<type>(static_cast<const void*>(value))
#else
# define png_voidcast(type, value) (value)
# define png_constcast(type, value) ((type)(value))
# define png_aligncast(type, value) ((void*)(value))
# define png_aligncastconst(type, value) ((const void*)(value))
# define voidcast(type, value) (value)
# define constcast(type, value) ((type)(value))
# define aligncast(type, value) ((void*)(value))
# define aligncastconst(type, value) ((const void*)(value))
#endif /* __cplusplus */
#if PNG_LIBPNG_VER < 10700
@@ -446,7 +446,7 @@ static void
make_random_bytes(png_uint_32* seed, void* pv, size_t size)
{
png_uint_32 u0 = seed[0], u1 = seed[1];
png_bytep bytes = png_voidcast(png_bytep, pv);
png_bytep bytes = voidcast(png_bytep, pv);
/* There are thirty-three bits; the next bit in the sequence is bit-33 XOR
* bit-20. The top 1 bit is in u1, the bottom 32 are in u0.
@@ -668,7 +668,7 @@ IDAT_list_extend(struct IDAT_list *tail)
if (length < tail->length) /* arithmetic overflow */
length = tail->length;
next = png_voidcast(IDAT_list*, malloc(IDAT_list_size(NULL, length)));
next = voidcast(IDAT_list*, malloc(IDAT_list_size(NULL, length)));
CLEAR(*next);
/* The caller must handle this: */
@@ -3535,7 +3535,7 @@ get_control(png_const_structrp png_ptr)
/* This just returns the (file*). The chunk and idat control structures
* don't always exist.
*/
struct control *control = png_voidcast(struct control*,
struct control *control = voidcast(struct control*,
png_get_error_ptr(png_ptr));
return &control->file;
}
@@ -3543,7 +3543,7 @@ get_control(png_const_structrp png_ptr)
static void
allocate(struct file *file, int allocate_idat)
{
struct control *control = png_voidcast(struct control*, file->alloc_ptr);
struct control *control = voidcast(struct control*, file->alloc_ptr);
if (allocate_idat)
{
@@ -3853,6 +3853,7 @@ usage(const char *prog)
int
main(int argc, const char **argv)
{
char temp_name[FILENAME_MAX+1];
const char * prog = *argv;
const char * outfile = NULL;
const char * suffix = NULL;
@@ -3955,7 +3956,6 @@ main(int argc, const char **argv)
else
{
size_t outlen = strlen(*argv);
char temp_name[FILENAME_MAX+1];
if (outfile == NULL) /* else this takes precedence */
{

View File

@@ -1,11 +1,12 @@
/*===
cexcept.h 2.0.1 (2008-Jul-19-Sat)
cexcept.h 2.0.1 (2008-Jul-19-Sat, modified 2015-Jun-03-Mon)
http://www.nicemice.net/cexcept/
Adam M. Costello
http://www.nicemice.net/amc/
An interface for exception-handling in ANSI C (C89 and subsequent ISO
standards), developed jointly with Cosmin Truta.
standards), developed jointly with Cosmin Truta. Revised by John Bowler,
June 2015, to declare exception_env and exception_prev "volatile".
Copyright (c) 2000-2008 Adam M. Costello and Cosmin Truta.
This software may be modified only if its author and version
@@ -210,7 +211,7 @@ struct exception_context { \
#define Try \
{ \
jmp_buf *exception__prev, exception__env; \
jmp_buf * volatile exception__prev, exception__env; \
exception__prev = the_exception_context->penv; \
the_exception_context->penv = &exception__env; \
if (setjmp(exception__env) == 0) { \