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