1 | /*- simpleover
|
---|
2 | *
|
---|
3 | * COPYRIGHT: Written by John Cunningham Bowler, 2015.
|
---|
4 | * To the extent possible under law, the author has waived all copyright and
|
---|
5 | * related or neighboring rights to this work. This work is published from:
|
---|
6 | * United States.
|
---|
7 | *
|
---|
8 | * Read several PNG files, which should have an alpha channel or transparency
|
---|
9 | * information, and composite them together to produce one or more 16-bit linear
|
---|
10 | * RGBA intermediates. This involves doing the correct 'over' composition to
|
---|
11 | * combine the alpha channels and corresponding data.
|
---|
12 | *
|
---|
13 | * Finally read an output (background) PNG using the 24-bit RGB format (the
|
---|
14 | * PNG will be composited on green (#00ff00) by default if it has an alpha
|
---|
15 | * channel), and apply the intermediate image generated above to specified
|
---|
16 | * locations in the image.
|
---|
17 | *
|
---|
18 | * The command line has the general format:
|
---|
19 | *
|
---|
20 | * simpleover <background.png> [output.png]
|
---|
21 | * {--sprite=width,height,name {[--at=x,y] {sprite.png}}}
|
---|
22 | * {--add=name {x,y}}
|
---|
23 | *
|
---|
24 | * The --sprite and --add options may occur multiple times. They are executed
|
---|
25 | * in order. --add may refer to any sprite already read.
|
---|
26 | *
|
---|
27 | * This code is intended to show how to composite multiple images together
|
---|
28 | * correctly. Apart from the libpng Simplified API the only work done in here
|
---|
29 | * is to combine multiple input PNG images into a single sprite; this involves
|
---|
30 | * a Porter-Duff 'over' operation and the input PNG images may, as a result,
|
---|
31 | * be regarded as being layered one on top of the other with the first (leftmost
|
---|
32 | * on the command line) being at the bottom and the last on the top.
|
---|
33 | */
|
---|
34 | #include <stddef.h>
|
---|
35 | #include <stdlib.h>
|
---|
36 | #include <string.h>
|
---|
37 | #include <stdio.h>
|
---|
38 | #include <errno.h>
|
---|
39 |
|
---|
40 | /* Normally use <png.h> here to get the installed libpng, but this is done to
|
---|
41 | * ensure the code picks up the local libpng implementation, so long as this
|
---|
42 | * file is linked against a sufficiently recent libpng (1.6+) it is ok to
|
---|
43 | * change this to <png.h>:
|
---|
44 | */
|
---|
45 | #include "../../png.h"
|
---|
46 |
|
---|
47 | #ifdef PNG_SIMPLIFIED_READ_SUPPORTED
|
---|
48 |
|
---|
49 | #define sprite_name_chars 15
|
---|
50 | struct sprite {
|
---|
51 | FILE *file;
|
---|
52 | png_uint_16p buffer;
|
---|
53 | unsigned int width;
|
---|
54 | unsigned int height;
|
---|
55 | char name[sprite_name_chars+1];
|
---|
56 | };
|
---|
57 |
|
---|
58 | #if 0 /* div by 65535 test program */
|
---|
59 | #include <math.h>
|
---|
60 | #include <stdio.h>
|
---|
61 |
|
---|
62 | int main(void) {
|
---|
63 | double err = 0;
|
---|
64 | unsigned int xerr = 0;
|
---|
65 | unsigned int r = 32769;
|
---|
66 | {
|
---|
67 | unsigned int x = 0;
|
---|
68 |
|
---|
69 | do {
|
---|
70 | unsigned int t = x + (x >> 16) /*+ (x >> 31)*/ + r;
|
---|
71 | double v = x, errtest;
|
---|
72 |
|
---|
73 | if (t < x) {
|
---|
74 | fprintf(stderr, "overflow: %u+%u -> %u\n", x, r, t);
|
---|
75 | return 1;
|
---|
76 | }
|
---|
77 |
|
---|
78 | v /= 65535;
|
---|
79 | errtest = v;
|
---|
80 | t >>= 16;
|
---|
81 | errtest -= t;
|
---|
82 |
|
---|
83 | if (errtest > err) {
|
---|
84 | err = errtest;
|
---|
85 | xerr = x;
|
---|
86 |
|
---|
87 | if (errtest >= .5) {
|
---|
88 | fprintf(stderr, "error: %u/65535 = %f, not %u, error %f\n",
|
---|
89 | x, v, t, errtest);
|
---|
90 | return 0;
|
---|
91 | }
|
---|
92 | }
|
---|
93 | } while (++x <= 65535U*65535U);
|
---|
94 | }
|
---|
95 |
|
---|
96 | printf("error %f @ %u\n", err, xerr);
|
---|
97 |
|
---|
98 | return 0;
|
---|
99 | }
|
---|
100 | #endif /* div by 65535 test program */
|
---|
101 |
|
---|
102 | static void
|
---|
103 | sprite_op(const struct sprite *sprite, int x_offset, int y_offset,
|
---|
104 | png_imagep image, const png_uint_16 *buffer)
|
---|
105 | {
|
---|
106 | /* This is where the Porter-Duff 'Over' operator is evaluated; change this
|
---|
107 | * code to change the operator (this could be parameterized). Any other
|
---|
108 | * image processing operation could be used here.
|
---|
109 | */
|
---|
110 |
|
---|
111 |
|
---|
112 | /* Check for an x or y offset that pushes any part of the image beyond the
|
---|
113 | * right or bottom of the sprite:
|
---|
114 | */
|
---|
115 | if ((y_offset < 0 || (unsigned)/*SAFE*/y_offset < sprite->height) &&
|
---|
116 | (x_offset < 0 || (unsigned)/*SAFE*/x_offset < sprite->width))
|
---|
117 | {
|
---|
118 | unsigned int y = 0;
|
---|
119 |
|
---|
120 | if (y_offset < 0)
|
---|
121 | y = -y_offset; /* Skip to first visible row */
|
---|
122 |
|
---|
123 | do
|
---|
124 | {
|
---|
125 | unsigned int x = 0;
|
---|
126 |
|
---|
127 | if (x_offset < 0)
|
---|
128 | x = -x_offset;
|
---|
129 |
|
---|
130 | do
|
---|
131 | {
|
---|
132 | /* In and out are RGBA values, so: */
|
---|
133 | const png_uint_16 *in_pixel = buffer + (y * image->width + x)*4;
|
---|
134 | png_uint_32 in_alpha = in_pixel[3];
|
---|
135 |
|
---|
136 | /* This is the optimized Porter-Duff 'Over' operation, when the
|
---|
137 | * input alpha is 0 the output is not changed.
|
---|
138 | */
|
---|
139 | if (in_alpha > 0)
|
---|
140 | {
|
---|
141 | png_uint_16 *out_pixel = sprite->buffer +
|
---|
142 | ((y+y_offset) * sprite->width + (x+x_offset))*4;
|
---|
143 |
|
---|
144 | /* This is the weight to apply to the output: */
|
---|
145 | in_alpha = 65535-in_alpha;
|
---|
146 |
|
---|
147 | if (in_alpha > 0)
|
---|
148 | {
|
---|
149 | /* The input must be composed onto the output. This means
|
---|
150 | * multiplying the current output pixel value by the inverse
|
---|
151 | * of the input alpha (1-alpha). A division is required but
|
---|
152 | * it is by the constant 65535. Approximate this as:
|
---|
153 | *
|
---|
154 | * (x + (x >> 16) + 32769) >> 16;
|
---|
155 | *
|
---|
156 | * This is exact (and does not overflow) for all values of
|
---|
157 | * x in the range 0..65535*65535. (Note that the calculation
|
---|
158 | * produces the closest integer; the maximum error is <0.5).
|
---|
159 | */
|
---|
160 | png_uint_32 tmp;
|
---|
161 |
|
---|
162 | # define compose(c)\
|
---|
163 | tmp = out_pixel[c] * in_alpha;\
|
---|
164 | tmp = (tmp + (tmp >> 16) + 32769) >> 16;\
|
---|
165 | out_pixel[c] = tmp + in_pixel[c]
|
---|
166 |
|
---|
167 | /* The following is very vectorizable... */
|
---|
168 | compose(0);
|
---|
169 | compose(1);
|
---|
170 | compose(2);
|
---|
171 | compose(3);
|
---|
172 | }
|
---|
173 |
|
---|
174 | else
|
---|
175 | out_pixel[0] = in_pixel[0],
|
---|
176 | out_pixel[1] = in_pixel[1],
|
---|
177 | out_pixel[2] = in_pixel[2],
|
---|
178 | out_pixel[3] = in_pixel[3];
|
---|
179 | }
|
---|
180 | }
|
---|
181 | while (++x < image->width);
|
---|
182 | }
|
---|
183 | while (++y < image->height);
|
---|
184 | }
|
---|
185 | }
|
---|
186 |
|
---|
187 | static int
|
---|
188 | create_sprite(struct sprite *sprite, int *argc, const char ***argv)
|
---|
189 | {
|
---|
190 | /* Read the arguments and create this sprite. The sprite buffer has already
|
---|
191 | * been allocated. This reads the input PNGs one by one in linear format,
|
---|
192 | * composes them onto the sprite buffer (the code in the function above)
|
---|
193 | * then saves the result, converting it on the fly to PNG RGBA 8-bit format.
|
---|
194 | */
|
---|
195 | while (*argc > 0)
|
---|
196 | {
|
---|
197 | char tombstone;
|
---|
198 | int x = 0, y = 0;
|
---|
199 |
|
---|
200 | if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-')
|
---|
201 | {
|
---|
202 | /* The only supported option is --at. */
|
---|
203 | if (sscanf((*argv)[0], "--at=%d,%d%c", &x, &y, &tombstone) != 2)
|
---|
204 | break; /* success; caller will parse this option */
|
---|
205 |
|
---|
206 | ++*argv, --*argc;
|
---|
207 | }
|
---|
208 |
|
---|
209 | else
|
---|
210 | {
|
---|
211 | /* The argument has to be a file name */
|
---|
212 | png_image image;
|
---|
213 |
|
---|
214 | image.version = PNG_IMAGE_VERSION;
|
---|
215 | image.opaque = NULL;
|
---|
216 |
|
---|
217 | if (png_image_begin_read_from_file(&image, (*argv)[0]))
|
---|
218 | {
|
---|
219 | png_uint_16p buffer;
|
---|
220 |
|
---|
221 | image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
|
---|
222 |
|
---|
223 | buffer = malloc(PNG_IMAGE_SIZE(image));
|
---|
224 |
|
---|
225 | if (buffer != NULL)
|
---|
226 | {
|
---|
227 | if (png_image_finish_read(&image, NULL/*background*/, buffer,
|
---|
228 | 0/*row_stride*/,
|
---|
229 | NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/))
|
---|
230 | {
|
---|
231 | /* This is the place where the Porter-Duff 'Over' operator
|
---|
232 | * needs to be done by this code. In fact, any image
|
---|
233 | * processing required can be done here; the data is in
|
---|
234 | * the correct format (linear, 16-bit) and source and
|
---|
235 | * destination are in memory.
|
---|
236 | */
|
---|
237 | sprite_op(sprite, x, y, &image, buffer);
|
---|
238 | free(buffer);
|
---|
239 | ++*argv, --*argc;
|
---|
240 | /* And continue to the next argument */
|
---|
241 | continue;
|
---|
242 | }
|
---|
243 |
|
---|
244 | else
|
---|
245 | {
|
---|
246 | free(buffer);
|
---|
247 | fprintf(stderr, "simpleover: read %s: %s\n", (*argv)[0],
|
---|
248 | image.message);
|
---|
249 | }
|
---|
250 | }
|
---|
251 |
|
---|
252 | else
|
---|
253 | {
|
---|
254 | fprintf(stderr, "simpleover: out of memory: %lu bytes\n",
|
---|
255 | (unsigned long)PNG_IMAGE_SIZE(image));
|
---|
256 |
|
---|
257 | /* png_image_free must be called if we abort the Simplified API
|
---|
258 | * read because of a problem detected in this code. If problems
|
---|
259 | * are detected in the Simplified API it cleans up itself.
|
---|
260 | */
|
---|
261 | png_image_free(&image);
|
---|
262 | }
|
---|
263 | }
|
---|
264 |
|
---|
265 | else
|
---|
266 | {
|
---|
267 | /* Failed to read the first argument: */
|
---|
268 | fprintf(stderr, "simpleover: %s: %s\n", (*argv)[0], image.message);
|
---|
269 | }
|
---|
270 |
|
---|
271 | return 0; /* failure */
|
---|
272 | }
|
---|
273 | }
|
---|
274 |
|
---|
275 | /* All the sprite operations have completed successfully. Save the RGBA
|
---|
276 | * buffer as a PNG using the simplified write API.
|
---|
277 | */
|
---|
278 | sprite->file = tmpfile();
|
---|
279 |
|
---|
280 | if (sprite->file != NULL)
|
---|
281 | {
|
---|
282 | png_image save;
|
---|
283 |
|
---|
284 | memset(&save, 0, sizeof save);
|
---|
285 | save.version = PNG_IMAGE_VERSION;
|
---|
286 | save.opaque = NULL;
|
---|
287 | save.width = sprite->width;
|
---|
288 | save.height = sprite->height;
|
---|
289 | save.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
|
---|
290 | save.flags = PNG_IMAGE_FLAG_FAST;
|
---|
291 | save.colormap_entries = 0;
|
---|
292 |
|
---|
293 | if (png_image_write_to_stdio(&save, sprite->file, 1/*convert_to_8_bit*/,
|
---|
294 | sprite->buffer, 0/*row_stride*/, NULL/*colormap*/))
|
---|
295 | {
|
---|
296 | /* Success; the buffer is no longer needed: */
|
---|
297 | free(sprite->buffer);
|
---|
298 | sprite->buffer = NULL;
|
---|
299 | return 1; /* ok */
|
---|
300 | }
|
---|
301 |
|
---|
302 | else
|
---|
303 | fprintf(stderr, "simpleover: write sprite %s: %s\n", sprite->name,
|
---|
304 | save.message);
|
---|
305 | }
|
---|
306 |
|
---|
307 | else
|
---|
308 | fprintf(stderr, "simpleover: sprite %s: could not allocate tmpfile: %s\n",
|
---|
309 | sprite->name, strerror(errno));
|
---|
310 |
|
---|
311 | return 0; /* fail */
|
---|
312 | }
|
---|
313 |
|
---|
314 | static int
|
---|
315 | add_sprite(png_imagep output, png_bytep out_buf, struct sprite *sprite,
|
---|
316 | int *argc, const char ***argv)
|
---|
317 | {
|
---|
318 | /* Given a --add argument naming this sprite, perform the operations listed
|
---|
319 | * in the following arguments. The arguments are expected to have the form
|
---|
320 | * (x,y), which is just an offset at which to add the sprite to the
|
---|
321 | * output.
|
---|
322 | */
|
---|
323 | while (*argc > 0)
|
---|
324 | {
|
---|
325 | char tombstone;
|
---|
326 | int x, y;
|
---|
327 |
|
---|
328 | if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-')
|
---|
329 | return 1; /* success */
|
---|
330 |
|
---|
331 | if (sscanf((*argv)[0], "%d,%d%c", &x, &y, &tombstone) == 2)
|
---|
332 | {
|
---|
333 | /* Now add the new image into the sprite data, but only if it
|
---|
334 | * will fit.
|
---|
335 | */
|
---|
336 | if (x < 0 || y < 0 ||
|
---|
337 | (unsigned)/*SAFE*/x >= output->width ||
|
---|
338 | (unsigned)/*SAFE*/y >= output->height ||
|
---|
339 | sprite->width > output->width-x ||
|
---|
340 | sprite->height > output->height-y)
|
---|
341 | {
|
---|
342 | fprintf(stderr, "simpleover: sprite %s @ (%d,%d) outside image\n",
|
---|
343 | sprite->name, x, y);
|
---|
344 | /* Could just skip this, but for the moment it is an error */
|
---|
345 | return 0; /* error */
|
---|
346 | }
|
---|
347 |
|
---|
348 | else
|
---|
349 | {
|
---|
350 | /* Since we know the sprite fits we can just read it into the
|
---|
351 | * output using the simplified API.
|
---|
352 | */
|
---|
353 | png_image in;
|
---|
354 |
|
---|
355 | in.version = PNG_IMAGE_VERSION;
|
---|
356 | rewind(sprite->file);
|
---|
357 |
|
---|
358 | if (png_image_begin_read_from_stdio(&in, sprite->file))
|
---|
359 | {
|
---|
360 | in.format = PNG_FORMAT_RGB; /* force compose */
|
---|
361 |
|
---|
362 | if (png_image_finish_read(&in, NULL/*background*/,
|
---|
363 | out_buf + (y*output->width + x)*3/*RGB*/,
|
---|
364 | output->width*3/*row_stride*/,
|
---|
365 | NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/))
|
---|
366 | {
|
---|
367 | ++*argv, --*argc;
|
---|
368 | continue;
|
---|
369 | }
|
---|
370 | }
|
---|
371 |
|
---|
372 | /* The read failed: */
|
---|
373 | fprintf(stderr, "simpleover: add sprite %s: %s\n", sprite->name,
|
---|
374 | in.message);
|
---|
375 | return 0; /* error */
|
---|
376 | }
|
---|
377 | }
|
---|
378 |
|
---|
379 | else
|
---|
380 | {
|
---|
381 | fprintf(stderr, "simpleover: --add='%s': invalid position %s\n",
|
---|
382 | sprite->name, (*argv)[0]);
|
---|
383 | return 0; /* error */
|
---|
384 | }
|
---|
385 | }
|
---|
386 |
|
---|
387 | return 1; /* ok */
|
---|
388 | }
|
---|
389 |
|
---|
390 | static int
|
---|
391 | simpleover_process(png_imagep output, png_bytep out_buf, int argc,
|
---|
392 | const char **argv)
|
---|
393 | {
|
---|
394 | int result = 1; /* success */
|
---|
395 | # define csprites 10/*limit*/
|
---|
396 | # define str(a) #a
|
---|
397 | int nsprites = 0;
|
---|
398 | struct sprite sprites[csprites];
|
---|
399 |
|
---|
400 | while (argc > 0)
|
---|
401 | {
|
---|
402 | result = 0; /* fail */
|
---|
403 |
|
---|
404 | if (strncmp(argv[0], "--sprite=", 9) == 0)
|
---|
405 | {
|
---|
406 | char tombstone;
|
---|
407 |
|
---|
408 | if (nsprites < csprites)
|
---|
409 | {
|
---|
410 | int n;
|
---|
411 |
|
---|
412 | sprites[nsprites].width = sprites[nsprites].height = 0;
|
---|
413 | sprites[nsprites].name[0] = 0;
|
---|
414 |
|
---|
415 | n = sscanf(argv[0], "--sprite=%u,%u,%" str(sprite_name_chars) "s%c",
|
---|
416 | &sprites[nsprites].width, &sprites[nsprites].height,
|
---|
417 | sprites[nsprites].name, &tombstone);
|
---|
418 |
|
---|
419 | if ((n == 2 || n == 3) &&
|
---|
420 | sprites[nsprites].width > 0 && sprites[nsprites].height > 0)
|
---|
421 | {
|
---|
422 | size_t buf_size, tmp;
|
---|
423 |
|
---|
424 | /* Default a name if not given. */
|
---|
425 | if (sprites[nsprites].name[0] == 0)
|
---|
426 | sprintf(sprites[nsprites].name, "sprite-%d", nsprites+1);
|
---|
427 |
|
---|
428 | /* Allocate a buffer for the sprite and calculate the buffer
|
---|
429 | * size:
|
---|
430 | */
|
---|
431 | buf_size = sizeof (png_uint_16 [4]);
|
---|
432 | buf_size *= sprites[nsprites].width;
|
---|
433 | buf_size *= sprites[nsprites].height;
|
---|
434 |
|
---|
435 | /* This can overflow a (size_t); check for this: */
|
---|
436 | tmp = buf_size;
|
---|
437 | tmp /= sprites[nsprites].width;
|
---|
438 | tmp /= sprites[nsprites].height;
|
---|
439 |
|
---|
440 | if (tmp == sizeof (png_uint_16 [4]))
|
---|
441 | {
|
---|
442 | sprites[nsprites].buffer = malloc(buf_size);
|
---|
443 | /* This buffer must be initialized to transparent: */
|
---|
444 | memset(sprites[nsprites].buffer, 0, buf_size);
|
---|
445 |
|
---|
446 | if (sprites[nsprites].buffer != NULL)
|
---|
447 | {
|
---|
448 | sprites[nsprites].file = NULL;
|
---|
449 | ++argv, --argc;
|
---|
450 |
|
---|
451 | if (create_sprite(sprites+nsprites++, &argc, &argv))
|
---|
452 | {
|
---|
453 | result = 1; /* still ok */
|
---|
454 | continue;
|
---|
455 | }
|
---|
456 |
|
---|
457 | break; /* error */
|
---|
458 | }
|
---|
459 | }
|
---|
460 |
|
---|
461 | /* Overflow, or OOM */
|
---|
462 | fprintf(stderr, "simpleover: %s: sprite too large\n", argv[0]);
|
---|
463 | break;
|
---|
464 | }
|
---|
465 |
|
---|
466 | else
|
---|
467 | {
|
---|
468 | fprintf(stderr, "simpleover: %s: invalid sprite (%u,%u)\n",
|
---|
469 | argv[0], sprites[nsprites].width, sprites[nsprites].height);
|
---|
470 | break;
|
---|
471 | }
|
---|
472 | }
|
---|
473 |
|
---|
474 | else
|
---|
475 | {
|
---|
476 | fprintf(stderr, "simpleover: %s: too many sprites\n", argv[0]);
|
---|
477 | break;
|
---|
478 | }
|
---|
479 | }
|
---|
480 |
|
---|
481 | else if (strncmp(argv[0], "--add=", 6) == 0)
|
---|
482 | {
|
---|
483 | const char *name = argv[0]+6;
|
---|
484 | int isprite = nsprites;
|
---|
485 |
|
---|
486 | ++argv, --argc;
|
---|
487 |
|
---|
488 | while (--isprite >= 0)
|
---|
489 | {
|
---|
490 | if (strcmp(sprites[isprite].name, name) == 0)
|
---|
491 | {
|
---|
492 | if (!add_sprite(output, out_buf, sprites+isprite, &argc, &argv))
|
---|
493 | goto out; /* error in add_sprite */
|
---|
494 |
|
---|
495 | break;
|
---|
496 | }
|
---|
497 | }
|
---|
498 |
|
---|
499 | if (isprite < 0) /* sprite not found */
|
---|
500 | {
|
---|
501 | fprintf(stderr, "simpleover: --add='%s': sprite not found\n", name);
|
---|
502 | break;
|
---|
503 | }
|
---|
504 | }
|
---|
505 |
|
---|
506 | else
|
---|
507 | {
|
---|
508 | fprintf(stderr, "simpleover: %s: unrecognized operation\n", argv[0]);
|
---|
509 | break;
|
---|
510 | }
|
---|
511 |
|
---|
512 | result = 1; /* ok */
|
---|
513 | }
|
---|
514 |
|
---|
515 | /* Clean up the cache of sprites: */
|
---|
516 | out:
|
---|
517 | while (--nsprites >= 0)
|
---|
518 | {
|
---|
519 | if (sprites[nsprites].buffer != NULL)
|
---|
520 | free(sprites[nsprites].buffer);
|
---|
521 |
|
---|
522 | if (sprites[nsprites].file != NULL)
|
---|
523 | (void)fclose(sprites[nsprites].file);
|
---|
524 | }
|
---|
525 |
|
---|
526 | return result;
|
---|
527 | }
|
---|
528 |
|
---|
529 | int main(int argc, const char **argv)
|
---|
530 | {
|
---|
531 | int result = 1; /* default to fail */
|
---|
532 |
|
---|
533 | if (argc >= 2)
|
---|
534 | {
|
---|
535 | int argi = 2;
|
---|
536 | const char *output = NULL;
|
---|
537 | png_image image;
|
---|
538 |
|
---|
539 | if (argc > 2 && argv[2][0] != '-'/*an operation*/)
|
---|
540 | {
|
---|
541 | output = argv[2];
|
---|
542 | argi = 3;
|
---|
543 | }
|
---|
544 |
|
---|
545 | image.version = PNG_IMAGE_VERSION;
|
---|
546 | image.opaque = NULL;
|
---|
547 |
|
---|
548 | if (png_image_begin_read_from_file(&image, argv[1]))
|
---|
549 | {
|
---|
550 | png_bytep buffer;
|
---|
551 |
|
---|
552 | image.format = PNG_FORMAT_RGB; /* 24-bit RGB */
|
---|
553 |
|
---|
554 | buffer = malloc(PNG_IMAGE_SIZE(image));
|
---|
555 |
|
---|
556 | if (buffer != NULL)
|
---|
557 | {
|
---|
558 | png_color background = {0, 0xff, 0}; /* fully saturated green */
|
---|
559 |
|
---|
560 | if (png_image_finish_read(&image, &background, buffer,
|
---|
561 | 0/*row_stride*/, NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP */))
|
---|
562 | {
|
---|
563 | /* At this point png_image_finish_read has cleaned up the
|
---|
564 | * allocated data in png_image, and only the buffer needs to be
|
---|
565 | * freed.
|
---|
566 | *
|
---|
567 | * Perform the remaining operations:
|
---|
568 | */
|
---|
569 | if (simpleover_process(&image, buffer, argc-argi, argv+argi))
|
---|
570 | {
|
---|
571 | /* Write the output: */
|
---|
572 | if ((output != NULL &&
|
---|
573 | png_image_write_to_file(&image, output,
|
---|
574 | 0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
|
---|
575 | NULL/*colormap*/)) ||
|
---|
576 | (output == NULL &&
|
---|
577 | png_image_write_to_stdio(&image, stdout,
|
---|
578 | 0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
|
---|
579 | NULL/*colormap*/)))
|
---|
580 | result = 0;
|
---|
581 |
|
---|
582 | else
|
---|
583 | fprintf(stderr, "simpleover: write %s: %s\n",
|
---|
584 | output == NULL ? "stdout" : output, image.message);
|
---|
585 | }
|
---|
586 |
|
---|
587 | /* else simpleover_process writes an error message */
|
---|
588 | }
|
---|
589 |
|
---|
590 | else
|
---|
591 | fprintf(stderr, "simpleover: read %s: %s\n", argv[1],
|
---|
592 | image.message);
|
---|
593 |
|
---|
594 | free(buffer);
|
---|
595 | }
|
---|
596 |
|
---|
597 | else
|
---|
598 | {
|
---|
599 | fprintf(stderr, "simpleover: out of memory: %lu bytes\n",
|
---|
600 | (unsigned long)PNG_IMAGE_SIZE(image));
|
---|
601 |
|
---|
602 | /* This is the only place where a 'free' is required; libpng does
|
---|
603 | * the cleanup on error and success, but in this case we couldn't
|
---|
604 | * complete the read because of running out of memory.
|
---|
605 | */
|
---|
606 | png_image_free(&image);
|
---|
607 | }
|
---|
608 | }
|
---|
609 |
|
---|
610 | else
|
---|
611 | {
|
---|
612 | /* Failed to read the first argument: */
|
---|
613 | fprintf(stderr, "simpleover: %s: %s\n", argv[1], image.message);
|
---|
614 | }
|
---|
615 | }
|
---|
616 |
|
---|
617 | else
|
---|
618 | {
|
---|
619 | /* Usage message */
|
---|
620 | fprintf(stderr,
|
---|
621 | "simpleover: usage: simpleover background.png [output.png]\n"
|
---|
622 | " Output 'background.png' as a 24-bit RGB PNG file in 'output.png'\n"
|
---|
623 | " or, if not given, stdout. 'background.png' will be composited\n"
|
---|
624 | " on fully saturated green.\n"
|
---|
625 | "\n"
|
---|
626 | " Optionally, before output, process additional PNG files:\n"
|
---|
627 | "\n"
|
---|
628 | " --sprite=width,height,name {[--at=x,y] {sprite.png}}\n"
|
---|
629 | " Produce a transparent sprite of size (width,height) and with\n"
|
---|
630 | " name 'name'.\n"
|
---|
631 | " For each sprite.png composite it using a Porter-Duff 'Over'\n"
|
---|
632 | " operation at offset (x,y) in the sprite (defaulting to (0,0)).\n"
|
---|
633 | " Input PNGs will be truncated to the area of the sprite.\n"
|
---|
634 | "\n"
|
---|
635 | " --add='name' {x,y}\n"
|
---|
636 | " Optionally, before output, composite a sprite, 'name', which\n"
|
---|
637 | " must have been previously produced using --sprite, at each\n"
|
---|
638 | " offset (x,y) in the output image. Each sprite must fit\n"
|
---|
639 | " completely within the output image.\n"
|
---|
640 | "\n"
|
---|
641 | " PNG files are processed in the order they occur on the command\n"
|
---|
642 | " line and thus the first PNG processed appears as the bottommost\n"
|
---|
643 | " in the output image.\n");
|
---|
644 | }
|
---|
645 |
|
---|
646 | return result;
|
---|
647 | }
|
---|
648 | #endif /* SIMPLIFIED_READ */
|
---|