simpleover.c 20 KB

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