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