VirtualBox

source: vbox/trunk/src/libs/libpng-1.6.45/contrib/tools/genpng.c@ 108378

最後變更 在這個檔案從108378是 103316,由 vboxsync 提交於 14 月 前

libpng-1.6.42: Applied and adjusted our libpng changes to 1.6.42. bugref:8515

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette