genpng.c 26 KB

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