19b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/*- genpng
29b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
39b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * COPYRIGHT: Written by John Cunningham Bowler, 2015.
49b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * To the extent possible under law, the author has waived all copyright and
59b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * related or neighboring rights to this work.  This work is published from:
69b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * United States.
79b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
89b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * Generate a PNG with an alpha channel, correctly.
99b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * This is a test case generator; the resultant PNG files are only of interest
119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * to those of us who care about whether the edges of circles are green, red,
129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * or yellow.
139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * The program generates an RGB+Alpha PNG of a given size containing the given
159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * shapes on a transparent background:
169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *  genpng width height { shape }
189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    shape ::= color width shape x1 y1 x2 y2
199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * 'color' is:
219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *  black white red green yellow blue brown purple pink orange gray cyan
239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * The point is to have colors that are linguistically meaningful plus that old
259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * bugbear of the department store dress murders, Cyan, the only color we argue
269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * about.
279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * 'shape' is:
299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *  circle: an ellipse
319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *  square: a rectangle
329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *  line: a straight line
339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * Each shape is followed by four numbers, these are two points in the output
359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * coordinate space (as real numbers) which describe the circle, square, or
369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * line.  The shape is filled if it is preceded by 'filled' (not valid for
379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * 'line') or is drawn with a line, in which case the width of the line must
389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * precede the shape.
399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * The whole set of information can be repeated as many times as desired:
419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    shape ::= color width shape x1 y1 x2 y2
439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    color ::= black|white|red|green|yellow|blue
459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    color ::= brown|purple|pink|orange|gray|cyan
469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    width ::= filled
479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    width ::= <number>
489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    shape ::= circle|square|line
499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    x1    ::= <number>
509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    x2    ::= <number>
519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    y1    ::= <number>
529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    y2    ::= <number>
539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * The output PNG is generated by down-sampling a 4x supersampled image using
559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * a bi-cubic filter.  The bi-cubic has a 2 (output) pixel width, so an 8x8
569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * array of super-sampled points contribute to each output pixel.  The value of
579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * a super-sampled point is found using an unfiltered, aliased, infinite
589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * precision image: Each shape from the last to the first is checked to see if
599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * the point is in the drawn area and, if it is, the color of the point is the
609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * color of the shape and the alpha is 1, if not the previous shape is checked.
619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * This is an aliased algorithm because no filtering is done; a point is either
639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * inside or outside each shape and 'close' points do not contribute to the
649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * sample.  The down-sampling is relied on to correct the error of not using
659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * a filter.
669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * The line end-caps are 'flat'; they go through the points.  The square line
689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * joins are mitres; the outside of the lines are continued to the point of
699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * intersection.
709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett */
719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <stddef.h>
729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <stdlib.h>
739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <string.h>
749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <stdio.h>
759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include <math.h>
769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/* Normally use <png.h> here to get the installed libpng, but this is done to
789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * ensure the code picks up the local libpng implementation:
799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett */
809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#include "../../png.h"
819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic const struct color
859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   const char *name;
879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double      red;
889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double      green;
899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double      blue;
909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett} colors[] =
919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/* color ::= black|white|red|green|yellow|blue
929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * color ::= brown|purple|pink|orange|gray|cyan
939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett */
949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "black",   0,    0,  0 },
969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "white",   1,    1,  1 },
979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "red",     1,    0,  0 },
989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "green",   0,    1,  0 },
999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "yellow",  1,    1,  0 },
1009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "blue",    0,    0,  1 },
1019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "brown",  .5, .125,  0 },
1029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "purple",  1,    0,  1 },
1039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "pink",    1,   .5, .5 },
1049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "orange",  1,   .5,  0 },
1059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "gray",    0,   .5, .5 },
1069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   { "cyan",    0,    1,  1 }
1079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett};
1089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define color_count ((sizeof colors)/(sizeof colors[0]))
1099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic const struct color *
1119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettcolor_of(const char *arg)
1129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
1139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   int icolor = color_count;
1149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   while (--icolor >= 0)
1169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
1179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (strcmp(colors[icolor].name, arg) == 0)
1189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return colors+icolor;
1199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
1209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   fprintf(stderr, "genpng: invalid color %s\n", arg);
1229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   exit(1);
1239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
1249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic double
1269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettwidth_of(const char *arg)
1279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
1289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (strcmp(arg, "filled") == 0)
1299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return 0;
1309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   else
1329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
1339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      char *ep = NULL;
1349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      double w = strtod(arg, &ep);
1359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (ep != NULL && *ep == 0 && w > 0)
1379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return w;
1389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
1399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   fprintf(stderr, "genpng: invalid line width %s\n", arg);
1419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   exit(1);
1429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
1439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic double
1459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettcoordinate_of(const char *arg)
1469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
1479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   char *ep = NULL;
1489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double w = strtod(arg, &ep);
1499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (ep != NULL && *ep == 0)
1519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return w;
1529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   fprintf(stderr, "genpng: invalid coordinate value %s\n", arg);
1549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   exit(1);
1559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
1569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstruct arg; /* forward declaration */
1589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Saretttypedef int (*shape_fn_ptr)(const struct arg *arg, double x, double y);
1609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* A function to determine if (x,y) is inside the shape.
1619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *
1629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * There are two implementations:
1639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *
1649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *    inside_fn: returns true if the point is inside
1659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *    check_fn:  returns;
1669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *       -1: the point is outside the shape by more than the filter width (2)
1679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *        0: the point may be inside the shape
1689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *       +1: the point is inside the shape by more than the filter width
1699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
1709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define OUTSIDE (-1)
1719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define INSIDE  (1)
1729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstruct arg
1749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
1759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   const struct color *color;
1769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   shape_fn_ptr        inside_fn;
1779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   shape_fn_ptr        check_fn;
1789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double              width; /* line width, 0 for 'filled' */
1799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double              x1, y1, x2, y2;
1809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett};
1819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
1829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/* IMPLEMENTATION NOTE:
1839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
1849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * We want the contribution of each shape to the sample corresponding to each
1859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * pixel.  This could be obtained by super sampling the image to infinite
1869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * dimensions, finding each point within the shape and assigning that a value
1879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * '1' while leaving every point outside the shape with value '0' then
1889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * downsampling to the image size with sinc; computationally very expensive.
1899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
1909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * Approximations are as follows:
1919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
1929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * 1) If the pixel coordinate is within the shape assume the sample has the
1939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    shape color and is opaque, else assume there is no contribution from
1949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    the shape.
1959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
1969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    This is the equivalent of aliased rendering or resampling an image with
1979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    a block filter.  The maximum error in the calculated alpha (which will
1989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    always be 0 or 1) is 0.5.
1999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
2009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * 2) If the shape is within a square of size 1x1 centered on the pixel assume
2019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    that the shape obscures an amount of the pixel equal to its area within
2029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    that square.
2039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
2049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    This is the equivalent of 'pixel coverage' alpha calculation or resampling
2059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    an image with a bi-linear filter.  The maximum error is over 0.2, but the
2069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    results are often acceptable.
2079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
2089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    This can be approximated by applying (1) to a super-sampled image then
2099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    downsampling with a bi-linear filter.  The error in the super-sampled
2109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    image is 0.5 per sample, but the resampling reduces this.
2119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
2129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * 3) Use a better filter with a super-sampled image; in the limit this is the
2139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    sinc() approach.
2149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
2159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * 4) Do the geometric calculation; a bivariate definite integral across the
2169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    shape, unfortunately this means evaluating Si(x), the integral of sinc(x),
2179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *    which is still a lot of math.
2189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
2199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * This code uses approach (3) with a bi-cubic filter and 8x super-sampling
2209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * and method (1) for the super-samples.  This means that the sample is either
2219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * 0 or 1, depending on whether the sub-pixel is within or outside the shape.
2229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * The bi-cubic weights are also fixed and the 16 required weights are
2239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * pre-computed here (note that the 'scale' setting will need to be changed if
2249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * 'super' is increased).
2259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
2269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * The code also calculates a sum to the edge of the filter. This is not
2279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * currently used by could be used to optimize the calculation.
2289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett */
2299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#if 0 /* bc code */
2309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettscale=10
2319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettsuper=8
2329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettdefine bicubic(x) {
2339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (x <= 1) return (1.5*x - 2.5)*x*x + 1;
2349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (x <  2) return (((2.5 - 0.5*x)*x - 4)*x + 2);
2359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return 0;
2369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
2379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettdefine sum(x) {
2389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   auto s;
2399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   s = 0;
2409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   while (x < 2*super) {
2419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      s = s + bicubic(x/super);
2429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      x = x + 1;
2439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
2449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return s;
2459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
2469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettdefine results(x) {
2479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   auto b, s;
2489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   b = bicubic(x/super);
2499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   s = sum(x);
2509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   print "   /*", x, "*/ { ", b, ", ", s, " }";
2529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return 1;
2539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
2549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettx=0
2559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettwhile (x<2*super) {
2569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   x = x + results(x)
2579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (x < 2*super) print ","
2589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   print "\n"
2599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
2609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettquit
2619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#endif
2629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define BICUBIC1(x) /*     |x| <= 1 */ ((1.5*(x)* - 2.5)*(x)*(x) + 1)
2649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define BICUBIC2(x) /* 1 < |x| <  2 */ (((2.5 - 0.5*(x))*(x) - 4)*(x) + 2)
2659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define FILTER_WEIGHT 9 /* Twice the first sum below */
2669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define FILTER_WIDTH  2 /* Actually half the width; -2..+2 */
2679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define FILTER_STEPS  8 /* steps per filter unit */
2689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic const double
2699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettbicubic[16][2] =
2709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
2719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* These numbers are exact; the weight for the filter is 1/9, but this
2729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * would make the numbers inexact, so it is not included here.
2739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
2749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /*          bicubic      sum        */
2759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* 0*/ { 1.0000000000, 4.5000000000 },
2769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* 1*/ {  .9638671875, 3.5000000000 },
2779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* 2*/ {  .8671875000, 2.5361328125 },
2789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* 3*/ {  .7275390625, 1.6689453125 },
2799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* 4*/ {  .5625000000,  .9414062500 },
2809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* 5*/ {  .3896484375,  .3789062500 },
2819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* 6*/ {  .2265625000, -.0107421875 },
2829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* 7*/ {  .0908203125, -.2373046875 },
2839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* 8*/ {            0, -.3281250000 },
2849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* 9*/ { -.0478515625, -.3281250000 },
2859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /*10*/ { -.0703125000, -.2802734375 },
2869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /*11*/ { -.0732421875, -.2099609375 },
2879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /*12*/ { -.0625000000, -.1367187500 },
2889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /*13*/ { -.0439453125, -.0742187500 },
2899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /*14*/ { -.0234375000, -.0302734375 },
2909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /*15*/ { -.0068359375, -.0068359375 }
2919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett};
2929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
2939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic double
2949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettalpha_calc(const struct arg *arg, double x, double y)
2959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
2969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* For [x-2..x+2],[y-2,y+2] calculate the weighted bicubic given a function
2979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * which tells us whether a point is inside or outside the shape.  First
2989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * check if we need to do this at all:
2999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
3009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   switch (arg->check_fn(arg, x, y))
3019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
3029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      case OUTSIDE:
3039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return 0; /* all samples outside the shape */
3049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      case INSIDE:
3069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return 1; /* all samples inside the shape */
3079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      default:
3099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
3109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         int dy;
3119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         double alpha = 0;
3129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#        define FILTER_D (FILTER_WIDTH*FILTER_STEPS-1)
3149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         for (dy=-FILTER_D; dy<=FILTER_D; ++dy)
3159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
3169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            double wy = bicubic[abs(dy)][0];
3179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            if (wy != 0)
3199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            {
3209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               double alphay = 0;
3219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               int dx;
3229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               for (dx=-FILTER_D; dx<=FILTER_D; ++dx)
3249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               {
3259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                  double wx = bicubic[abs(dx)][0];
3269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
32706f1087a94f1e48298e89d77ccc51a0ced871958Matt Sarett                  if (wx != 0 && arg->inside_fn(arg, x+dx/16, y+dy/16))
3289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett                     alphay += wx;
3299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               }
3309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               alpha += wy * alphay;
3329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            }
3339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
3349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         /* This needs to be weighted for each dimension: */
3369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return alpha / (FILTER_WEIGHT*FILTER_WEIGHT);
3379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
3389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
3399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
3409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/* These are the shape functions. */
3429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/* "square",
3439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * { inside_square_filled, check_square_filled },
3449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * { inside_square, check_square }
3459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett */
3469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
3479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettsquare_check(double x, double y, double x1, double y1, double x2, double y2)
3489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Is x,y inside the square (x1,y1)..(x2,y2)? */
3499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
3509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Do a modified Cohen-Sutherland on one point, bit patterns that indicate
3519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * 'outside' are:
3529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *
3539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *   x<x1 | x<y1 | x<x2 | x<y2
3549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *    0      x      0      x     To the right
3559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *    1      x      1      x     To the left
3569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *    x      0      x      0     Below
3579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *    x      1      x      1     Above
3589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *
3599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * So 'inside' is (x<x1) != (x<x2) && (y<y1) != (y<y2);
3609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
3619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return ((x<x1) ^ (x<x2)) & ((y<y1) ^ (y<y2));
3629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
3639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
3659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettinside_square_filled(const struct arg *arg, double x, double y)
3669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
3679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return square_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2);
3689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
3699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
3719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettsquare_check_line(const struct arg *arg, double x, double y, double w)
3729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Check for a point being inside the boundaries implied by the given arg
3739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * and assuming a width 2*w each side of the boundaries.  This returns the
3749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * 'check' INSIDE/OUTSIDE/0 result but note the semantics:
3759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *
3769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *          +--------------+
3779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *          |              |   OUTSIDE
3789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *          |   INSIDE     |
3799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *          |              |
3809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *          +--------------+
3819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *
3829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * And '0' means within the line boundaries.
3839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
3849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
3859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double cx = (arg->x1+arg->x2)/2;
3869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double wx = fabs(arg->x1-arg->x2)/2;
3879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double cy = (arg->y1+arg->y2)/2;
3889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double wy = fabs(arg->y1-arg->y2)/2;
3899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
3909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (square_check(x, y, cx-wx-w, cy-wy-w, cx+wx+w, cy+wy+w))
3919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
3929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* Inside, but maybe too far; check for the redundant case where
3939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       * the lines overlap:
3949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       */
3959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      wx -= w;
3969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      wy -= w;
3979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (wx > 0 && wy > 0 && square_check(x, y, cx-wx, cy-wy, cx+wx, cy+wy))
3989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return INSIDE; /* between (inside) the boundary lines. */
3999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return 0; /* inside the lines themselves. */
4019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
4029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return OUTSIDE; /* outside the boundary lines. */
4049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
4059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
4079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettcheck_square_filled(const struct arg *arg, double x, double y)
4089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
4099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* The filter extends +/-FILTER_WIDTH each side of each output point, so
4109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * the check has to expand and contract the square by that amount; '0'
4119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * means close enough to the edge of the square that the bicubic filter has
4129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * to be run, OUTSIDE means alpha==0, INSIDE means alpha==1.
4139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
4149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return square_check_line(arg, x, y, FILTER_WIDTH);
4159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
4169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
4189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettinside_square(const struct arg *arg, double x, double y)
4199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
4209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Return true if within the drawn lines, else false, no need to distinguish
4219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * INSIDE vs OUTSIDE here:
4229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
4239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return square_check_line(arg, x, y, arg->width/2) == 0;
4249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
4259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
4279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettcheck_square(const struct arg *arg, double x, double y)
4289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
4299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* So for this function a result of 'INSIDE' means inside the actual lines.
4309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
4319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double w = arg->width/2;
4329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (square_check_line(arg, x, y, w+FILTER_WIDTH) == 0)
4349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
4359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* Somewhere close to the boundary lines. If far enough inside one of
4369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       * them then we can return INSIDE:
4379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       */
4389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      w -= FILTER_WIDTH;
4399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (w > 0 && square_check_line(arg, x, y, w) == 0)
4419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return INSIDE;
4429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* Point is somewhere in the filter region: */
4449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return 0;
4459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
4469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   else /* Inside or outside the square by more than w+FILTER_WIDTH. */
4489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return OUTSIDE;
4499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
4509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/* "circle",
4529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * { inside_circle_filled, check_circle_filled },
4539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * { inside_circle, check_circle }
4549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett *
4559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * The functions here are analoguous to the square ones; however, they check
4569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * the corresponding ellipse as opposed to the rectangle.
4579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett */
4589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
4599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettcircle_check(double x, double y, double x1, double y1, double x2, double y2)
4609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
4619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (square_check(x, y, x1, y1, x2, y2))
4629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
4639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* Inside the square, so maybe inside the circle too: */
4649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      const double cx = (x1 + x2)/2;
4659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      const double cy = (y1 + y2)/2;
4669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      const double dx = x1 - x2;
4679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      const double dy = y1 - y2;
4689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      x = (x - cx)/dx;
4709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      y = (y - cy)/dy;
4719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* It is outside if the distance from the center is more than half the
4739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       * diameter:
4749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       */
4759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return x*x+y*y < .25;
4769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
4779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return 0; /* outside */
4799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
4809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
4829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettinside_circle_filled(const struct arg *arg, double x, double y)
4839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
4849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return circle_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2);
4859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
4869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
4889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettcircle_check_line(const struct arg *arg, double x, double y, double w)
4899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Check for a point being inside the boundaries implied by the given arg
4909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * and assuming a width 2*w each side of the boundaries.  This function has
4919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * the same semantic as square_check_line but tests the circle.
4929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
4939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
4949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double cx = (arg->x1+arg->x2)/2;
4959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double wx = fabs(arg->x1-arg->x2)/2;
4969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double cy = (arg->y1+arg->y2)/2;
4979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double wy = fabs(arg->y1-arg->y2)/2;
4989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
4999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (circle_check(x, y, cx-wx-w, cy-wy-w, cx+wx+w, cy+wy+w))
5009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
5019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* Inside, but maybe too far; check for the redundant case where
5029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       * the lines overlap:
5039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       */
5049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      wx -= w;
5059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      wy -= w;
5069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (wx > 0 && wy > 0 && circle_check(x, y, cx-wx, cy-wy, cx+wx, cy+wy))
5079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return INSIDE; /* between (inside) the boundary lines. */
5089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return 0; /* inside the lines themselves. */
5109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
5119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return OUTSIDE; /* outside the boundary lines. */
5139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
5149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
5169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettcheck_circle_filled(const struct arg *arg, double x, double y)
5179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
5189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return circle_check_line(arg, x, y, FILTER_WIDTH);
5199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
5209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
5229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettinside_circle(const struct arg *arg, double x, double y)
5239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
5249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return circle_check_line(arg, x, y, arg->width/2) == 0;
5259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
5269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
5289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettcheck_circle(const struct arg *arg, double x, double y)
5299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
5309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Exactly as the 'square' code.  */
5319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double w = arg->width/2;
5329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (circle_check_line(arg, x, y, w+FILTER_WIDTH) == 0)
5349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
5359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      w -= FILTER_WIDTH;
5369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (w > 0 && circle_check_line(arg, x, y, w) == 0)
5389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return INSIDE;
5399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* Point is somewhere in the filter region: */
5419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return 0;
5429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
5439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   else /* Inside or outside the square by more than w+FILTER_WIDTH. */
5459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return OUTSIDE;
5469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
5479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/* "line",
5499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * { NULL, NULL },  There is no 'filled' line.
5509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * { inside_line, check_line }
5519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett */
5529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
5539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettline_check(double x, double y, double x1, double y1, double x2, double y2,
5549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double w, double expand)
5559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
5569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Shift all the points to (arg->x1, arg->y1) */
5579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double lx = x2 - x1;
5589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double ly = y2 - y1;
5599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double len2 = lx*lx + ly*ly;
5609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double cross, dot;
5619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   x -= x1;
5639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   y -= y1;
5649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* The dot product is the distance down the line, the cross product is
5669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * the distance away from the line:
5679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *
56806f1087a94f1e48298e89d77ccc51a0ced871958Matt Sarett    *    distance = |cross| / sqrt(len2)
5699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
5709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   cross = x * ly - y * lx;
5719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* If 'distance' is more than w the point is definitely outside the line:
5739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *
5749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *     distance >= w
5759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *     |cross|  >= w * sqrt(len2)
5769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *     cross^2  >= w^2 * len2:
5779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
5789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (cross*cross >= (w+expand)*(w+expand)*len2)
5799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return 0; /* outside */
5809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Now find the distance *along* the line; this comes from the dot product
5829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * lx.x+ly.y. The actual distance (in pixels) is:
5839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *
5849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *   distance = dot / sqrt(len2)
5859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
5869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   dot = lx * x + ly * y;
5879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* The test for 'outside' is:
5899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *
5909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *    distance < 0 || distance > sqrt(len2)
5919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *                 -> dot / sqrt(len2) > sqrt(len2)
5929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *                 -> dot > len2
5939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    *
5949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * But 'expand' is used for the filter width and needs to be handled too:
5959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
5969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return dot > -expand && dot < len2+expand;
5979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
5989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
5999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
6009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettinside_line(const struct arg *arg, double x, double y)
6019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
6029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2, 0);
6039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
6049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic int
6069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettcheck_line(const struct arg *arg, double x, double y)
6079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
6089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* The end caps of the line must be checked too; it's not enough just to
6099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * widen the line by FILTER_WIDTH; 'expand' exists for this purpose:
6109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
6119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2,
6129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       FILTER_WIDTH))
6139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
6149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* Inside the line+filter; far enough inside that the filter isn't
6159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       * required?
6169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       */
6179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (arg->width > 2*FILTER_WIDTH &&
6189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett          line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2,
6199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             -FILTER_WIDTH))
6209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return INSIDE;
6219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return 0;
6239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
6249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return OUTSIDE;
6269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
6279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic const struct
6299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
6309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   const char    *name;
6319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   shape_fn_ptr   function[2/*fill,line*/][2];
6329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#  define         FN_INSIDE 0
6339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#  define         FN_CHECK 1
6349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett} shape_defs[] =
6359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
6369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {  "square",
6379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {  { inside_square_filled, check_square_filled },
6389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         { inside_square, check_square } }
6399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   },
6409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {  "circle",
6419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {  { inside_circle_filled, check_circle_filled },
6429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         { inside_circle, check_circle } }
6439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   },
6449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {  "line",
6459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {  { NULL, NULL },
6469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         { inside_line, check_line } }
6479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
6489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett};
6499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define shape_count ((sizeof shape_defs)/(sizeof shape_defs[0]))
6519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic shape_fn_ptr
6539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettshape_of(const char *arg, double width, int f)
6549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
6559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   unsigned int i;
6569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   for (i=0; i<shape_count; ++i) if (strcmp(shape_defs[i].name, arg) == 0)
6589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
6599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      shape_fn_ptr fn = shape_defs[i].function[width != 0][f];
6609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (fn != NULL)
6629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return fn;
6639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      fprintf(stderr, "genpng: %s %s not supported\n",
6659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         width == 0 ? "filled" : "unfilled", arg);
6669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      exit(1);
6679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
6689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   fprintf(stderr, "genpng: %s: not a valid shape name\n", arg);
6709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   exit(1);
6719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
6729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic void
6749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettparse_arg(struct arg *arg, const char **argv/*7 arguments*/)
6759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
6769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* shape ::= color width shape x1 y1 x2 y2 */
6779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   arg->color = color_of(argv[0]);
6789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   arg->width = width_of(argv[1]);
6799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   arg->inside_fn = shape_of(argv[2], arg->width, FN_INSIDE);
6809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   arg->check_fn = shape_of(argv[2], arg->width, FN_CHECK);
6819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   arg->x1 = coordinate_of(argv[3]);
6829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   arg->y1 = coordinate_of(argv[4]);
6839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   arg->x2 = coordinate_of(argv[5]);
6849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   arg->y2 = coordinate_of(argv[6]);
6859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
6869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic png_uint_32
6889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettread_wh(const char *name, const char *str)
6899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* read a PNG width or height */
6909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
6919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   char *ep = NULL;
6929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   unsigned long ul = strtoul(str, &ep, 10);
6939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (ep != NULL && *ep == 0 && ul > 0 && ul <= 0x7fffffff)
6959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      return (png_uint_32)/*SAFE*/ul;
6969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
6979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   fprintf(stderr, "genpng: %s: invalid number %s\n", name, str);
6989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   exit(1);
6999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
7009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettstatic void
7029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettpixel(png_uint_16p p, struct arg *args, int nargs, double x, double y)
7039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
7049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* Fill in the pixel by checking each shape (args[nargs]) for effects on
7059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * the corresponding sample:
7069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
7079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   double r=0, g=0, b=0, a=0;
7089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   while (--nargs >= 0 && a != 1)
7109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
7119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* NOTE: alpha_calc can return a value outside the range 0..1 with the
7129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       * bicubic filter.
7139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       */
7149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      const double alpha = alpha_calc(args+nargs, x, y) * (1-a);
7159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      r += alpha * args[nargs].color->red;
7179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      g += alpha * args[nargs].color->green;
7189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      b += alpha * args[nargs].color->blue;
7199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      a += alpha;
7209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
7219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* 'a' may be negative or greater than 1; if it is, negative clamp the
7239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    * pixel to 0 if >1 clamp r/g/b:
7249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett    */
72506f1087a94f1e48298e89d77ccc51a0ced871958Matt Sarett   if (a > 0)
7269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
7279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (a > 1)
7289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
7299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (r > 1) r = 1;
7309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (g > 1) g = 1;
7319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (b > 1) b = 1;
7329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         a = 1;
7339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
7349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* And fill in the pixel: */
7369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      p[0] = (png_uint_16)/*SAFE*/round(r * 65535);
7379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      p[1] = (png_uint_16)/*SAFE*/round(g * 65535);
7389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      p[2] = (png_uint_16)/*SAFE*/round(b * 65535);
7399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      p[3] = (png_uint_16)/*SAFE*/round(a * 65535);
7409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
7419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   else
7439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      p[3] = p[2] = p[1] = p[0] = 0;
7449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
7459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettint
7479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettmain(int argc, const char **argv)
7489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett{
7499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   int convert_to_8bit = 0;
7509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   /* There is one option: --8bit: */
7529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (argc > 1 && strcmp(argv[1], "--8bit") == 0)
7539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      --argc, ++argv, convert_to_8bit = 1;
7549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   if (argc >= 3)
7569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
7579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      png_uint_16p buffer;
7589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      int nshapes;
7599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      png_image image;
7609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#     define max_shapes 256
7619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      struct arg arg_list[max_shapes];
7629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* The libpng Simplified API write code requires a fully initialized
7649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       * structure.
7659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett       */
7669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      memset(&image, 0, sizeof image);
7679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      image.version = PNG_IMAGE_VERSION;
7689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      image.opaque = NULL;
7699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      image.width = read_wh("width", argv[1]);
7709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      image.height = read_wh("height", argv[2]);
7719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
7729b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      image.flags = 0;
7739b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      image.colormap_entries = 0;
7749b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7759b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* Check the remainder of the arguments */
7769b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      for (nshapes=0; 3+7*(nshapes+1) <= argc && nshapes < max_shapes;
7779b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett           ++nshapes)
7789b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         parse_arg(arg_list+nshapes, argv+3+7*nshapes);
7799b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7809b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (3+7*nshapes != argc)
7819b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
7829b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         fprintf(stderr, "genpng: %s: too many arguments\n", argv[3+7*nshapes]);
7839b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         return 1;
7849b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
7859b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* Create the buffer: */
7879b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      buffer = malloc(PNG_IMAGE_SIZE(image));
7889b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      if (buffer != NULL)
7909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      {
7919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         png_uint_32 y;
7929b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7939b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         /* Write each row... */
7949b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         for (y=0; y<image.height; ++y)
7959b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
7969b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            png_uint_32 x;
7979b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
7989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            /* Each pixel in each row: */
7999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            for (x=0; x<image.width; ++x)
8009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               pixel(buffer + 4*(x + y*image.width), arg_list, nshapes, x, y);
8019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
8029b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
8039b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         /* Write the result (to stdout) */
8049b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         if (png_image_write_to_stdio(&image, stdout, convert_to_8bit,
8059b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett             buffer, 0/*row_stride*/, NULL/*colormap*/))
8069b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         {
8079b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            free(buffer);
8089b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            return 0; /* success */
8099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         }
8109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
8119b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         else
8129b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett            fprintf(stderr, "genpng: write stdout: %s\n", image.message);
8139b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
8149b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         free(buffer);
8159b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      }
8169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
8179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      else
8189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         fprintf(stderr, "genpng: out of memory: %lu bytes\n",
8199b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett               (unsigned long)PNG_IMAGE_SIZE(image));
8209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
8219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
8229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   else
8239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   {
8249b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      /* Wrong number of arguments */
8259b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett      fprintf(stderr, "genpng: usage: genpng [--8bit] width height {shape}\n"
8269b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " Generate a transparent PNG in RGBA (truecolor+alpha) format\n"
8279b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " containing the given shape or shapes.  Shapes are defined:\n"
8289b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
8299b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  shape ::= color width shape x1 y1 x2 y2\n"
8309b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  color ::= black|white|red|green|yellow|blue\n"
8319b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  color ::= brown|purple|pink|orange|gray|cyan\n"
8329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  width ::= filled|<number>\n"
8339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  shape ::= circle|square|line\n"
8349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  x1,x2 ::= <number>\n"
8359b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  y1,y2 ::= <number>\n"
8369b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
8379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " Numbers are floating point numbers describing points relative to\n"
8389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " the top left of the output PNG as pixel coordinates.  The 'width'\n"
8399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " parameter is either the width of the line (in output pixels) used\n"
8409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " to draw the shape or 'filled' to indicate that the shape should\n"
8419b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " be filled with the color.\n"
8429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
8439b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " Colors are interpreted loosely to give access to the eight full\n"
8449b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " intensity RGB values:\n"
8459b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
8469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  black, red, green, blue, yellow, cyan, purple, white,\n"
8479b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
8489b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " Cyan is full intensity blue+green; RGB(0,1,1), plus the following\n"
8499b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " lower intensity values:\n"
8509b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
8519b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  brown:  red+orange:  RGB(0.5, 0.125, 0) (dark red+orange)\n"
8529b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  pink:   red+white:   RGB(1.0, 0.5,   0.5)\n"
8539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  orange: red+yellow:  RGB(1.0, 0.5,   0)\n"
8549b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "  gray:   black+white: RGB(0.5, 0.5,   0.5)\n"
8559b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
8569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " The RGB values are selected to make detection of aliasing errors\n"
8579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " easy. The names are selected to make the description of errors\n"
8589b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " easy.\n"
8599b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         "\n"
8609b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " The PNG is written to stdout, if --8bit is given a 32bpp RGBA sRGB\n"
8619b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " file is produced, otherwise a 64bpp RGBA linear encoded file is\n"
8629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett         " written.\n");
8639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   }
8649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett
8659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett   return 1;
8669b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett}
8679b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#endif /* SIMPLIFIED_WRITE && STDIO */
868