]> git.tdb.fi Git - ext/libpng.git/blob - contrib/tools/genpng.c
Import libpng 1.6.39
[ext/libpng.git] / contrib / tools / genpng.c
1 /* genpng
2  *
3  * COPYRIGHT: Written by John Cunningham Bowler, 2015.
4  * Revised by Glenn Randers-Pehrson, 2017, to add buffer-size check.
5  * To the extent possible under law, the authors have waived all copyright and
6  * related or neighboring rights to this work.  This work is published from:
7  * United States.
8  *
9  * Generate a PNG with an alpha channel, correctly.
10  *
11  * This is a test case generator; the resultant PNG files are only of interest
12  * to those of us who care about whether the edges of circles are green, red,
13  * or yellow.
14  *
15  * The program generates an RGB+Alpha PNG of a given size containing the given
16  * shapes on a transparent background:
17  *
18  *  genpng width height { shape }
19  *    shape ::= color width shape x1 y1 x2 y2
20  *
21  * 'color' is:
22  *
23  *  black white red green yellow blue brown purple pink orange gray cyan
24  *
25  * The point is to have colors that are linguistically meaningful plus that old
26  * bugbear of the department store dress murders, Cyan, the only color we argue
27  * about.
28  *
29  * 'shape' is:
30  *
31  *  circle: an ellipse
32  *  square: a rectangle
33  *  line: a straight line
34  *
35  * Each shape is followed by four numbers, these are two points in the output
36  * coordinate space (as real numbers) which describe the circle, square, or
37  * line.  The shape is filled if it is preceded by 'filled' (not valid for
38  * 'line') or is drawn with a line, in which case the width of the line must
39  * precede the shape.
40  *
41  * The whole set of information can be repeated as many times as desired:
42  *
43  *    shape ::= color width shape x1 y1 x2 y2
44  *
45  *    color ::= black|white|red|green|yellow|blue
46  *    color ::= brown|purple|pink|orange|gray|cyan
47  *    width ::= filled
48  *    width ::= <number>
49  *    shape ::= circle|square|line
50  *    x1    ::= <number>
51  *    x2    ::= <number>
52  *    y1    ::= <number>
53  *    y2    ::= <number>
54  *
55  * The output PNG is generated by down-sampling a 4x supersampled image using
56  * a bi-cubic filter.  The bi-cubic has a 2 (output) pixel width, so an 8x8
57  * array of super-sampled points contribute to each output pixel.  The value of
58  * a super-sampled point is found using an unfiltered, aliased, infinite
59  * precision image: Each shape from the last to the first is checked to see if
60  * the point is in the drawn area and, if it is, the color of the point is the
61  * color of the shape and the alpha is 1, if not the previous shape is checked.
62  *
63  * This is an aliased algorithm because no filtering is done; a point is either
64  * inside or outside each shape and 'close' points do not contribute to the
65  * sample.  The down-sampling is relied on to correct the error of not using
66  * a filter.
67  *
68  * The line end-caps are 'flat'; they go through the points.  The square line
69  * joins are mitres; the outside of the lines are continued to the point of
70  * intersection.
71  */
72
73 #include <stddef.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <stdio.h>
77 #include <math.h>
78
79 /* Normally use <png.h> here to get the installed libpng, but this is done to
80  * ensure the code picks up the local libpng implementation:
81  */
82 #include "../../png.h"
83
84 #if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
85
86 static const struct color
87 {
88    const char *name;
89    double      red;
90    double      green;
91    double      blue;
92 } colors[] =
93 /* color ::= black|white|red|green|yellow|blue
94  * color ::= brown|purple|pink|orange|gray|cyan
95  */
96 {
97    { "black",   0,    0,  0 },
98    { "white",   1,    1,  1 },
99    { "red",     1,    0,  0 },
100    { "green",   0,    1,  0 },
101    { "yellow",  1,    1,  0 },
102    { "blue",    0,    0,  1 },
103    { "brown",  .5, .125,  0 },
104    { "purple",  1,    0,  1 },
105    { "pink",    1,   .5, .5 },
106    { "orange",  1,   .5,  0 },
107    { "gray",    0,   .5, .5 },
108    { "cyan",    0,    1,  1 }
109 };
110 #define color_count ((sizeof colors)/(sizeof colors[0]))
111
112 static const struct color *
113 color_of(const char *arg)
114 {
115    int icolor = color_count;
116
117    while (--icolor >= 0)
118    {
119       if (strcmp(colors[icolor].name, arg) == 0)
120          return colors+icolor;
121    }
122
123    fprintf(stderr, "genpng: invalid color %s\n", arg);
124    exit(1);
125 }
126
127 static double
128 width_of(const char *arg)
129 {
130    if (strcmp(arg, "filled") == 0)
131       return 0;
132
133    else
134    {
135       char *ep = NULL;
136       double w = strtod(arg, &ep);
137
138       if (ep != NULL && *ep == 0 && w > 0)
139          return w;
140    }
141
142    fprintf(stderr, "genpng: invalid line width %s\n", arg);
143    exit(1);
144 }
145
146 static double
147 coordinate_of(const char *arg)
148 {
149    char *ep = NULL;
150    double w = strtod(arg, &ep);
151
152    if (ep != NULL && *ep == 0)
153       return w;
154
155    fprintf(stderr, "genpng: invalid coordinate value %s\n", arg);
156    exit(1);
157 }
158
159 struct arg; /* forward declaration */
160
161 typedef int (*shape_fn_ptr)(const struct arg *arg, double x, double y);
162    /* A function to determine if (x,y) is inside the shape.
163     *
164     * There are two implementations:
165     *
166     *    inside_fn: returns true if the point is inside
167     *    check_fn:  returns;
168     *       -1: the point is outside the shape by more than the filter width (2)
169     *        0: the point may be inside the shape
170     *       +1: the point is inside the shape by more than the filter width
171     */
172 #define OUTSIDE (-1)
173 #define INSIDE  (1)
174
175 struct arg
176 {
177    const struct color *color;
178    shape_fn_ptr        inside_fn;
179    shape_fn_ptr        check_fn;
180    double              width; /* line width, 0 for 'filled' */
181    double              x1, y1, x2, y2;
182 };
183
184 /* IMPLEMENTATION NOTE:
185  *
186  * We want the contribution of each shape to the sample corresponding to each
187  * pixel.  This could be obtained by super sampling the image to infinite
188  * dimensions, finding each point within the shape and assigning that a value
189  * '1' while leaving every point outside the shape with value '0' then
190  * downsampling to the image size with sinc; computationally very expensive.
191  *
192  * Approximations are as follows:
193  *
194  * 1) If the pixel coordinate is within the shape assume the sample has the
195  *    shape color and is opaque, else assume there is no contribution from
196  *    the shape.
197  *
198  *    This is the equivalent of aliased rendering or resampling an image with
199  *    a block filter.  The maximum error in the calculated alpha (which will
200  *    always be 0 or 1) is 0.5.
201  *
202  * 2) If the shape is within a square of size 1x1 centered on the pixel assume
203  *    that the shape obscures an amount of the pixel equal to its area within
204  *    that square.
205  *
206  *    This is the equivalent of 'pixel coverage' alpha calculation or resampling
207  *    an image with a bi-linear filter.  The maximum error is over 0.2, but the
208  *    results are often acceptable.
209  *
210  *    This can be approximated by applying (1) to a super-sampled image then
211  *    downsampling with a bi-linear filter.  The error in the super-sampled
212  *    image is 0.5 per sample, but the resampling reduces this.
213  *
214  * 3) Use a better filter with a super-sampled image; in the limit this is the
215  *    sinc() approach.
216  *
217  * 4) Do the geometric calculation; a bivariate definite integral across the
218  *    shape, unfortunately this means evaluating Si(x), the integral of sinc(x),
219  *    which is still a lot of math.
220  *
221  * This code uses approach (3) with a bi-cubic filter and 8x super-sampling
222  * and method (1) for the super-samples.  This means that the sample is either
223  * 0 or 1, depending on whether the sub-pixel is within or outside the shape.
224  * The bi-cubic weights are also fixed and the 16 required weights are
225  * pre-computed here (note that the 'scale' setting will need to be changed if
226  * 'super' is increased).
227  *
228  * The code also calculates a sum to the edge of the filter. This is not
229  * currently used by could be used to optimize the calculation.
230  */
231 #if 0 /* bc code */
232 scale=10
233 super=8
234 define bicubic(x) {
235    if (x <= 1) return (1.5*x - 2.5)*x*x + 1;
236    if (x <  2) return (((2.5 - 0.5*x)*x - 4)*x + 2);
237    return 0;
238 }
239 define sum(x) {
240    auto s;
241    s = 0;
242    while (x < 2*super) {
243       s = s + bicubic(x/super);
244       x = x + 1;
245    }
246    return s;
247 }
248 define results(x) {
249    auto b, s;
250    b = bicubic(x/super);
251    s = sum(x);
252
253    print "   /*", x, "*/ { ", b, ", ", s, " }";
254    return 1;
255 }
256 x=0
257 while (x<2*super) {
258    x = x + results(x)
259    if (x < 2*super) print ","
260    print "\n"
261 }
262 quit
263 #endif
264
265 #define BICUBIC1(x) /*     |x| <= 1 */ ((1.5*(x)* - 2.5)*(x)*(x) + 1)
266 #define BICUBIC2(x) /* 1 < |x| <  2 */ (((2.5 - 0.5*(x))*(x) - 4)*(x) + 2)
267 #define FILTER_WEIGHT 9 /* Twice the first sum below */
268 #define FILTER_WIDTH  2 /* Actually half the width; -2..+2 */
269 #define FILTER_STEPS  8 /* steps per filter unit */
270 static const double
271 bicubic[16][2] =
272 {
273    /* These numbers are exact; the weight for the filter is 1/9, but this
274     * would make the numbers inexact, so it is not included here.
275     */
276    /*          bicubic      sum        */
277    /* 0*/ { 1.0000000000, 4.5000000000 },
278    /* 1*/ {  .9638671875, 3.5000000000 },
279    /* 2*/ {  .8671875000, 2.5361328125 },
280    /* 3*/ {  .7275390625, 1.6689453125 },
281    /* 4*/ {  .5625000000,  .9414062500 },
282    /* 5*/ {  .3896484375,  .3789062500 },
283    /* 6*/ {  .2265625000, -.0107421875 },
284    /* 7*/ {  .0908203125, -.2373046875 },
285    /* 8*/ {            0, -.3281250000 },
286    /* 9*/ { -.0478515625, -.3281250000 },
287    /*10*/ { -.0703125000, -.2802734375 },
288    /*11*/ { -.0732421875, -.2099609375 },
289    /*12*/ { -.0625000000, -.1367187500 },
290    /*13*/ { -.0439453125, -.0742187500 },
291    /*14*/ { -.0234375000, -.0302734375 },
292    /*15*/ { -.0068359375, -.0068359375 }
293 };
294
295 static double
296 alpha_calc(const struct arg *arg, double x, double y)
297 {
298    /* For [x-2..x+2],[y-2,y+2] calculate the weighted bicubic given a function
299     * which tells us whether a point is inside or outside the shape.  First
300     * check if we need to do this at all:
301     */
302    switch (arg->check_fn(arg, x, y))
303    {
304       case OUTSIDE:
305          return 0; /* all samples outside the shape */
306
307       case INSIDE:
308          return 1; /* all samples inside the shape */
309
310       default:
311       {
312          int dy;
313          double alpha = 0;
314
315 #        define FILTER_D (FILTER_WIDTH*FILTER_STEPS-1)
316          for (dy=-FILTER_D; dy<=FILTER_D; ++dy)
317          {
318             double wy = bicubic[abs(dy)][0];
319
320             if (wy != 0)
321             {
322                double alphay = 0;
323                int dx;
324
325                for (dx=-FILTER_D; dx<=FILTER_D; ++dx)
326                {
327                   double wx = bicubic[abs(dx)][0];
328
329                   if (wx != 0 && arg->inside_fn(arg, x+dx/16, y+dy/16))
330                      alphay += wx;
331                }
332
333                alpha += wy * alphay;
334             }
335          }
336
337          /* This needs to be weighted for each dimension: */
338          return alpha / (FILTER_WEIGHT*FILTER_WEIGHT);
339       }
340    }
341 }
342
343 /* These are the shape functions. */
344 /* "square",
345  * { inside_square_filled, check_square_filled },
346  * { inside_square, check_square }
347  */
348 static int
349 square_check(double x, double y, double x1, double y1, double x2, double y2)
350    /* Is x,y inside the square (x1,y1)..(x2,y2)? */
351 {
352    /* Do a modified Cohen-Sutherland on one point, bit patterns that indicate
353     * 'outside' are:
354     *
355     *   x<x1 | x<y1 | x<x2 | x<y2
356     *    0      x      0      x     To the right
357     *    1      x      1      x     To the left
358     *    x      0      x      0     Below
359     *    x      1      x      1     Above
360     *
361     * So 'inside' is (x<x1) != (x<x2) && (y<y1) != (y<y2);
362     */
363    return ((x<x1) ^ (x<x2)) & ((y<y1) ^ (y<y2));
364 }
365
366 static int
367 inside_square_filled(const struct arg *arg, double x, double y)
368 {
369    return square_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2);
370 }
371
372 static int
373 square_check_line(const struct arg *arg, double x, double y, double w)
374    /* Check for a point being inside the boundaries implied by the given arg
375     * and assuming a width 2*w each side of the boundaries.  This returns the
376     * 'check' INSIDE/OUTSIDE/0 result but note the semantics:
377     *
378     *          +--------------+
379     *          |              |   OUTSIDE
380     *          |   INSIDE     |
381     *          |              |
382     *          +--------------+
383     *
384     * And '0' means within the line boundaries.
385     */
386 {
387    double cx = (arg->x1+arg->x2)/2;
388    double wx = fabs(arg->x1-arg->x2)/2;
389    double cy = (arg->y1+arg->y2)/2;
390    double wy = fabs(arg->y1-arg->y2)/2;
391
392    if (square_check(x, y, cx-wx-w, cy-wy-w, cx+wx+w, cy+wy+w))
393    {
394       /* Inside, but maybe too far; check for the redundant case where
395        * the lines overlap:
396        */
397       wx -= w;
398       wy -= w;
399       if (wx > 0 && wy > 0 && square_check(x, y, cx-wx, cy-wy, cx+wx, cy+wy))
400          return INSIDE; /* between (inside) the boundary lines. */
401
402       return 0; /* inside the lines themselves. */
403    }
404
405    return OUTSIDE; /* outside the boundary lines. */
406 }
407
408 static int
409 check_square_filled(const struct arg *arg, double x, double y)
410 {
411    /* The filter extends +/-FILTER_WIDTH each side of each output point, so
412     * the check has to expand and contract the square by that amount; '0'
413     * means close enough to the edge of the square that the bicubic filter has
414     * to be run, OUTSIDE means alpha==0, INSIDE means alpha==1.
415     */
416    return square_check_line(arg, x, y, FILTER_WIDTH);
417 }
418
419 static int
420 inside_square(const struct arg *arg, double x, double y)
421 {
422    /* Return true if within the drawn lines, else false, no need to distinguish
423     * INSIDE vs OUTSIDE here:
424     */
425    return square_check_line(arg, x, y, arg->width/2) == 0;
426 }
427
428 static int
429 check_square(const struct arg *arg, double x, double y)
430 {
431    /* So for this function a result of 'INSIDE' means inside the actual lines.
432     */
433    double w = arg->width/2;
434
435    if (square_check_line(arg, x, y, w+FILTER_WIDTH) == 0)
436    {
437       /* Somewhere close to the boundary lines. If far enough inside one of
438        * them then we can return INSIDE:
439        */
440       w -= FILTER_WIDTH;
441
442       if (w > 0 && square_check_line(arg, x, y, w) == 0)
443          return INSIDE;
444
445       /* Point is somewhere in the filter region: */
446       return 0;
447    }
448
449    else /* Inside or outside the square by more than w+FILTER_WIDTH. */
450       return OUTSIDE;
451 }
452
453 /* "circle",
454  * { inside_circle_filled, check_circle_filled },
455  * { inside_circle, check_circle }
456  *
457  * The functions here are analogous to the square ones; however, they check
458  * the corresponding ellipse as opposed to the rectangle.
459  */
460 static int
461 circle_check(double x, double y, double x1, double y1, double x2, double y2)
462 {
463    if (square_check(x, y, x1, y1, x2, y2))
464    {
465       /* Inside the square, so maybe inside the circle too: */
466       const double cx = (x1 + x2)/2;
467       const double cy = (y1 + y2)/2;
468       const double dx = x1 - x2;
469       const double dy = y1 - y2;
470
471       x = (x - cx)/dx;
472       y = (y - cy)/dy;
473
474       /* It is outside if the distance from the center is more than half the
475        * diameter:
476        */
477       return x*x+y*y < .25;
478    }
479
480    return 0; /* outside */
481 }
482
483 static int
484 inside_circle_filled(const struct arg *arg, double x, double y)
485 {
486    return circle_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2);
487 }
488
489 static int
490 circle_check_line(const struct arg *arg, double x, double y, double w)
491    /* Check for a point being inside the boundaries implied by the given arg
492     * and assuming a width 2*w each side of the boundaries.  This function has
493     * the same semantic as square_check_line but tests the circle.
494     */
495 {
496    double cx = (arg->x1+arg->x2)/2;
497    double wx = fabs(arg->x1-arg->x2)/2;
498    double cy = (arg->y1+arg->y2)/2;
499    double wy = fabs(arg->y1-arg->y2)/2;
500
501    if (circle_check(x, y, cx-wx-w, cy-wy-w, cx+wx+w, cy+wy+w))
502    {
503       /* Inside, but maybe too far; check for the redundant case where
504        * the lines overlap:
505        */
506       wx -= w;
507       wy -= w;
508       if (wx > 0 && wy > 0 && circle_check(x, y, cx-wx, cy-wy, cx+wx, cy+wy))
509          return INSIDE; /* between (inside) the boundary lines. */
510
511       return 0; /* inside the lines themselves. */
512    }
513
514    return OUTSIDE; /* outside the boundary lines. */
515 }
516
517 static int
518 check_circle_filled(const struct arg *arg, double x, double y)
519 {
520    return circle_check_line(arg, x, y, FILTER_WIDTH);
521 }
522
523 static int
524 inside_circle(const struct arg *arg, double x, double y)
525 {
526    return circle_check_line(arg, x, y, arg->width/2) == 0;
527 }
528
529 static int
530 check_circle(const struct arg *arg, double x, double y)
531 {
532    /* Exactly as the 'square' code.  */
533    double w = arg->width/2;
534
535    if (circle_check_line(arg, x, y, w+FILTER_WIDTH) == 0)
536    {
537       w -= FILTER_WIDTH;
538
539       if (w > 0 && circle_check_line(arg, x, y, w) == 0)
540          return INSIDE;
541
542       /* Point is somewhere in the filter region: */
543       return 0;
544    }
545
546    else /* Inside or outside the square by more than w+FILTER_WIDTH. */
547       return OUTSIDE;
548 }
549
550 /* "line",
551  * { NULL, NULL },  There is no 'filled' line.
552  * { inside_line, check_line }
553  */
554 static int
555 line_check(double x, double y, double x1, double y1, double x2, double y2,
556    double w, double expand)
557 {
558    /* Shift all the points to (arg->x1, arg->y1) */
559    double lx = x2 - x1;
560    double ly = y2 - y1;
561    double len2 = lx*lx + ly*ly;
562    double cross, dot;
563
564    x -= x1;
565    y -= y1;
566
567    /* The dot product is the distance down the line, the cross product is
568     * the distance away from the line:
569     *
570     *    distance = |cross| / sqrt(len2)
571     */
572    cross = x * ly - y * lx;
573
574    /* If 'distance' is more than w the point is definitely outside the line:
575     *
576     *     distance >= w
577     *     |cross|  >= w * sqrt(len2)
578     *     cross^2  >= w^2 * len2:
579     */
580    if (cross*cross >= (w+expand)*(w+expand)*len2)
581       return 0; /* outside */
582
583    /* Now find the distance *along* the line; this comes from the dot product
584     * lx.x+ly.y. The actual distance (in pixels) is:
585     *
586     *   distance = dot / sqrt(len2)
587     */
588    dot = lx * x + ly * y;
589
590    /* The test for 'outside' is:
591     *
592     *    distance < 0 || distance > sqrt(len2)
593     *                 -> dot / sqrt(len2) > sqrt(len2)
594     *                 -> dot > len2
595     *
596     * But 'expand' is used for the filter width and needs to be handled too:
597     */
598    return dot > -expand && dot < len2+expand;
599 }
600
601 static int
602 inside_line(const struct arg *arg, double x, double y)
603 {
604    return line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2, 0);
605 }
606
607 static int
608 check_line(const struct arg *arg, double x, double y)
609 {
610    /* The end caps of the line must be checked too; it's not enough just to
611     * widen the line by FILTER_WIDTH; 'expand' exists for this purpose:
612     */
613    if (line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2,
614        FILTER_WIDTH))
615    {
616       /* Inside the line+filter; far enough inside that the filter isn't
617        * required?
618        */
619       if (arg->width > 2*FILTER_WIDTH &&
620           line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2,
621              -FILTER_WIDTH))
622          return INSIDE;
623
624       return 0;
625    }
626
627    return OUTSIDE;
628 }
629
630 static const struct
631 {
632    const char    *name;
633    shape_fn_ptr   function[2/*fill,line*/][2];
634 #  define         FN_INSIDE 0
635 #  define         FN_CHECK 1
636 } shape_defs[] =
637 {
638    {  "square",
639       {  { inside_square_filled, check_square_filled },
640          { inside_square, check_square } }
641    },
642    {  "circle",
643       {  { inside_circle_filled, check_circle_filled },
644          { inside_circle, check_circle } }
645    },
646    {  "line",
647       {  { NULL, NULL },
648          { inside_line, check_line } }
649    }
650 };
651
652 #define shape_count ((sizeof shape_defs)/(sizeof shape_defs[0]))
653
654 static shape_fn_ptr
655 shape_of(const char *arg, double width, int f)
656 {
657    unsigned int i;
658
659    for (i=0; i<shape_count; ++i) if (strcmp(shape_defs[i].name, arg) == 0)
660    {
661       shape_fn_ptr fn = shape_defs[i].function[width != 0][f];
662
663       if (fn != NULL)
664          return fn;
665
666       fprintf(stderr, "genpng: %s %s not supported\n",
667          width == 0 ? "filled" : "unfilled", arg);
668       exit(1);
669    }
670
671    fprintf(stderr, "genpng: %s: not a valid shape name\n", arg);
672    exit(1);
673 }
674
675 static void
676 parse_arg(struct arg *arg, const char **argv/*7 arguments*/)
677 {
678    /* shape ::= color width shape x1 y1 x2 y2 */
679    arg->color = color_of(argv[0]);
680    arg->width = width_of(argv[1]);
681    arg->inside_fn = shape_of(argv[2], arg->width, FN_INSIDE);
682    arg->check_fn = shape_of(argv[2], arg->width, FN_CHECK);
683    arg->x1 = coordinate_of(argv[3]);
684    arg->y1 = coordinate_of(argv[4]);
685    arg->x2 = coordinate_of(argv[5]);
686    arg->y2 = coordinate_of(argv[6]);
687 }
688
689 static png_uint_32
690 read_wh(const char *name, const char *str)
691    /* read a PNG width or height */
692 {
693    char *ep = NULL;
694    unsigned long ul = strtoul(str, &ep, 10);
695
696    if (ep != NULL && *ep == 0 && ul > 0 && ul <= 0x7fffffff)
697       return (png_uint_32)/*SAFE*/ul;
698
699    fprintf(stderr, "genpng: %s: invalid number %s\n", name, str);
700    exit(1);
701 }
702
703 static void
704 pixel(png_uint_16p p, struct arg *args, int nargs, double x, double y)
705 {
706    /* Fill in the pixel by checking each shape (args[nargs]) for effects on
707     * the corresponding sample:
708     */
709    double r=0, g=0, b=0, a=0;
710
711    while (--nargs >= 0 && a != 1)
712    {
713       /* NOTE: alpha_calc can return a value outside the range 0..1 with the
714        * bicubic filter.
715        */
716       const double alpha = alpha_calc(args+nargs, x, y) * (1-a);
717
718       r += alpha * args[nargs].color->red;
719       g += alpha * args[nargs].color->green;
720       b += alpha * args[nargs].color->blue;
721       a += alpha;
722    }
723
724    /* 'a' may be negative or greater than 1; if it is, negative clamp the
725     * pixel to 0 if >1 clamp r/g/b:
726     */
727    if (a > 0)
728    {
729       if (a > 1)
730       {
731          if (r > 1) r = 1;
732          if (g > 1) g = 1;
733          if (b > 1) b = 1;
734          a = 1;
735       }
736
737       /* And fill in the pixel: */
738       p[0] = (png_uint_16)/*SAFE*/round(r * 65535);
739       p[1] = (png_uint_16)/*SAFE*/round(g * 65535);
740       p[2] = (png_uint_16)/*SAFE*/round(b * 65535);
741       p[3] = (png_uint_16)/*SAFE*/round(a * 65535);
742    }
743
744    else
745       p[3] = p[2] = p[1] = p[0] = 0;
746 }
747
748 int
749 main(int argc, const char **argv)
750 {
751    int convert_to_8bit = 0;
752
753    /* There is one option: --8bit: */
754    if (argc > 1 && strcmp(argv[1], "--8bit") == 0)
755       --argc, ++argv, convert_to_8bit = 1;
756
757    if (argc >= 3)
758    {
759       png_uint_16p buffer;
760       int nshapes;
761       png_image image;
762 #     define max_shapes 256
763       struct arg arg_list[max_shapes];
764
765       /* The libpng Simplified API write code requires a fully initialized
766        * structure.
767        */
768       memset(&image, 0, sizeof image);
769       image.version = PNG_IMAGE_VERSION;
770       image.opaque = NULL;
771       image.width = read_wh("width", argv[1]);
772       image.height = read_wh("height", argv[2]);
773       image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
774       image.flags = 0;
775       image.colormap_entries = 0;
776
777       /* Check the remainder of the arguments */
778       for (nshapes=0; 3+7*(nshapes+1) <= argc && nshapes < max_shapes;
779            ++nshapes)
780          parse_arg(arg_list+nshapes, argv+3+7*nshapes);
781
782       if (3+7*nshapes != argc)
783       {
784          fprintf(stderr, "genpng: %s: too many arguments\n", argv[3+7*nshapes]);
785          return 1;
786       }
787
788 #if 1
789      /* TO do: determine whether this guard against overflow is necessary.
790       * This comment in png.h indicates that it should be safe: "libpng will
791       * refuse to process an image where such an overflow would occur", but
792       * I don't see where the image gets rejected when the buffer is too
793       * large before the malloc is attempted.
794       */
795       if (image.height > ((size_t)(-1))/(8*image.width)) {
796          fprintf(stderr, "genpng: image buffer would be too big");
797          return 1;
798       }
799 #endif
800
801       /* Create the buffer: */
802       buffer = malloc(PNG_IMAGE_SIZE(image));
803
804       if (buffer != NULL)
805       {
806          png_uint_32 y;
807
808          /* Write each row... */
809          for (y=0; y<image.height; ++y)
810          {
811             png_uint_32 x;
812
813             /* Each pixel in each row: */
814             for (x=0; x<image.width; ++x)
815                pixel(buffer + 4*(x + y*image.width), arg_list, nshapes, x, y);
816          }
817
818          /* Write the result (to stdout) */
819          if (png_image_write_to_stdio(&image, stdout, convert_to_8bit,
820              buffer, 0/*row_stride*/, NULL/*colormap*/))
821          {
822             free(buffer);
823             return 0; /* success */
824          }
825
826          else
827             fprintf(stderr, "genpng: write stdout: %s\n", image.message);
828
829          free(buffer);
830       }
831
832       else
833          fprintf(stderr, "genpng: out of memory: %lu bytes\n",
834                (unsigned long)PNG_IMAGE_SIZE(image));
835    }
836
837    else
838    {
839       /* Wrong number of arguments */
840       fprintf(stderr, "genpng: usage: genpng [--8bit] width height {shape}\n"
841          " Generate a transparent PNG in RGBA (truecolor+alpha) format\n"
842          " containing the given shape or shapes.  Shapes are defined:\n"
843          "\n"
844          "  shape ::= color width shape x1 y1 x2 y2\n"
845          "  color ::= black|white|red|green|yellow|blue\n"
846          "  color ::= brown|purple|pink|orange|gray|cyan\n"
847          "  width ::= filled|<number>\n"
848          "  shape ::= circle|square|line\n"
849          "  x1,x2 ::= <number>\n"
850          "  y1,y2 ::= <number>\n"
851          "\n"
852          " Numbers are floating point numbers describing points relative to\n"
853          " the top left of the output PNG as pixel coordinates.  The 'width'\n"
854          " parameter is either the width of the line (in output pixels) used\n"
855          " to draw the shape or 'filled' to indicate that the shape should\n"
856          " be filled with the color.\n"
857          "\n"
858          " Colors are interpreted loosely to give access to the eight full\n"
859          " intensity RGB values:\n"
860          "\n"
861          "  black, red, green, blue, yellow, cyan, purple, white,\n"
862          "\n"
863          " Cyan is full intensity blue+green; RGB(0,1,1), plus the following\n"
864          " lower intensity values:\n"
865          "\n"
866          "  brown:  red+orange:  RGB(0.5, 0.125, 0) (dark red+orange)\n"
867          "  pink:   red+white:   RGB(1.0, 0.5,   0.5)\n"
868          "  orange: red+yellow:  RGB(1.0, 0.5,   0)\n"
869          "  gray:   black+white: RGB(0.5, 0.5,   0.5)\n"
870          "\n"
871          " The RGB values are selected to make detection of aliasing errors\n"
872          " easy. The names are selected to make the description of errors\n"
873          " easy.\n"
874          "\n"
875          " The PNG is written to stdout, if --8bit is given a 32bpp RGBA sRGB\n"
876          " file is produced, otherwise a 64bpp RGBA linear encoded file is\n"
877          " written.\n");
878    }
879
880    return 1;
881 }
882 #endif /* SIMPLIFIED_WRITE && STDIO */