diff --git a/ANNOUNCE b/ANNOUNCE index 463cf7fa3..6908c9631 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -1,4 +1,4 @@ -Libpng 1.6.18beta03 - May 3, 2015 +Libpng 1.6.18beta03 - May 5, 2015 This is not intended to be a public release. It will be replaced within a few weeks by a public version or by another test version. @@ -38,11 +38,12 @@ Version 1.6.18beta02 [April 26, 2015] Suppressed some warnings from the Borland C++ 5.5.1/5.82 compiler (Bug report by Viktor Szaka'ts). -Version 1.6.18beta03 [May 3, 2015] +Version 1.6.18beta03 [May 5, 2015] Replaced "unexpected" with an integer (0xabadca11) in pngset.c where a long was expected, to avoid a compiler warning when PNG_DEBUG > 1. Added contrib/examples/simpleover.c, to demonstrate how to handle alpha compositing of multiple images, using the "simplified API" + and an example PNG generation tool, contrib/examples/genpng.c (John Bowler). Send comments/corrections/commendations to png-mng-implement at lists.sf.net diff --git a/CHANGES b/CHANGES index 49963a07f..34bef16cf 100644 --- a/CHANGES +++ b/CHANGES @@ -5218,11 +5218,12 @@ Version 1.6.18beta02 [April 26, 2015] Suppressed some warnings from the Borland C++ 5.5.1/5.82 compiler (Bug report by Viktor Szaka'ts). -Version 1.6.18beta03 [May 3, 2015] +Version 1.6.18beta03 [May 5, 2015] Replaced "unexpected" with an integer (0xabadca11) in pngset.c where a long was expected, to avoid a compiler warning when PNG_DEBUG > 1. Added contrib/examples/simpleover.c, to demonstrate how to handle alpha compositing of multiple images, using the "simplified API" + and an example PNG generation tool, contrib/examples/genpng.c (John Bowler). Send comments/corrections/commendations to png-mng-implement at lists.sf.net diff --git a/contrib/examples/simpleover.c b/contrib/examples/simpleover.c index bd0382951..d74cd978e 100644 --- a/contrib/examples/simpleover.c +++ b/contrib/examples/simpleover.c @@ -210,6 +210,7 @@ create_sprite(struct sprite *sprite, int *argc, const char ***argv) png_image image; image.version = PNG_IMAGE_VERSION; + image.opaque = NULL; if (png_image_begin_read_from_file(&image, (*argv)[0])) { @@ -540,6 +541,8 @@ int main(int argc, const char **argv) } image.version = PNG_IMAGE_VERSION; + image.opaque = NULL; + if (png_image_begin_read_from_file(&image, argv[1])) { png_bytep buffer; diff --git a/contrib/tools/genpng.c b/contrib/tools/genpng.c new file mode 100644 index 000000000..1864a98c0 --- /dev/null +++ b/contrib/tools/genpng.c @@ -0,0 +1,864 @@ +/*- 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 ::= + * shape ::= circle|square|line + * x1 ::= + * x2 ::= + * y1 ::= + * y2 ::= + * + * 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 +#include +#include +#include +#include + +/* Normally use here to get the installed libpng, but this is done to + * ensure the code picks up the local libpng implementation: + */ +#include "../../png.h" + +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: + * + * xx1, 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; icolor = 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\n" + " shape ::= circle|square|line\n" + " x1,x2 ::= \n" + " y1,y2 ::= \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; +} diff --git a/png.h b/png.h index 214fe60d2..fdc1018e4 100644 --- a/png.h +++ b/png.h @@ -1,7 +1,7 @@ /* png.h - header file for PNG reference library * - * libpng version 1.6.18beta03, April 26, 2015 + * libpng version 1.6.18beta03, May 5, 2015 * * Copyright (c) 1998-2015 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) @@ -12,7 +12,7 @@ * Authors and maintainers: * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger - * libpng versions 0.97, January 1998, through 1.6.18beta03, April 26, 2015: Glenn + * libpng versions 0.97, January 1998, through 1.6.18beta03, May 5, 2015: Glenn * See also "Contributing Authors", below. * * Note about libpng version numbers: @@ -248,7 +248,7 @@ * * This code is released under the libpng license. * - * libpng versions 1.2.6, August 15, 2004, through 1.6.18beta03, April 26, 2015, are + * libpng versions 1.2.6, August 15, 2004, through 1.6.18beta03, May 5, 2015, are * Copyright (c) 2004, 2006-2015 Glenn Randers-Pehrson, and are * distributed according to the same disclaimer and license as libpng-1.2.5 * with the following individual added to the list of Contributing Authors: @@ -360,7 +360,7 @@ * Y2K compliance in libpng: * ========================= * - * April 26, 2015 + * May 5, 2015 * * Since the PNG Development group is an ad-hoc body, we can't make * an official declaration. @@ -430,7 +430,7 @@ /* Version information for png.h - this should match the version in png.c */ #define PNG_LIBPNG_VER_STRING "1.6.18beta03" #define PNG_HEADER_VERSION_STRING \ - " libpng version 1.6.18beta03 - April 26, 2015\n" + " libpng version 1.6.18beta03 - May 5, 2015\n" #define PNG_LIBPNG_VER_SONUM 16 #define PNG_LIBPNG_VER_DLLNUM 16 @@ -2784,8 +2784,9 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); * * To read a PNG file using the simplified API: * - * 1) Declare a 'png_image' structure (see below) on the stack and set the - * version field to PNG_IMAGE_VERSION. + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) * 2) Call the appropriate png_image_begin_read... function. * 3) Set the png_image 'format' member to the required sample format. * 4) Allocate a buffer for the image and, if required, the color-map.