PageRenderTime 59ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/Engine/lib/lmng/contrib/msvc/makemng/makemng.c

https://github.com/bobbyzhu/Torque3D
C | 1765 lines | 1368 code | 314 blank | 83 comment | 359 complexity | fc33358173f4552a3c710bb00bcc9f89 MD5 | raw file
Possible License(s): BSD-2-Clause, CPL-1.0, BSD-3-Clause, LGPL-3.0, GPL-2.0, LGPL-2.0, MIT
  1. /*
  2. * This program is free software; you can redistribute it and/or modify
  3. * it under the terms of the GNU General Public License as published by
  4. * the Free Software Foundation; either version 2 of the License, or
  5. * (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program; if not, write to the Free Software
  14. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  15. */
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <stdint.h>
  19. #include <string.h>
  20. #include <malloc.h>
  21. #include <zlib.h>
  22. #include <libmng.h>
  23. #ifdef WIN32
  24. # include <getopt.h>
  25. #else
  26. # include <unistd.h>
  27. #endif
  28. struct options
  29. {
  30. char *inmask;
  31. char *inpath;
  32. char *outfile;
  33. int framerate;
  34. char verbose;
  35. char backimage;
  36. char deltamask;
  37. int sectorsize;
  38. int fullrects;
  39. } opts;
  40. // externals
  41. char** make_file_list (const char* pattern, int* pnum_entries);
  42. void free_file_list (char** list);
  43. // internals
  44. int error (int errn, const char* fmt, const char* s);
  45. void verbose (const char* fmt, const char* s);
  46. void verbose_d (const char* fmt, int val);
  47. void parse_arguments (int argc, char *argv[], struct options *opts);
  48. int read_file_list (void);
  49. void calc_mng_dims (void);
  50. void select_back_image (void);
  51. void delta_images (void);
  52. int write_mng_file (void);
  53. typedef union
  54. {
  55. struct
  56. {
  57. unsigned char r, g, b, a;
  58. } bchan;
  59. unsigned char channels[4];
  60. unsigned int value;
  61. } RGBA;
  62. typedef struct _file_info
  63. {
  64. FILE* f;
  65. char* fname;
  66. char* fmode;
  67. int w, h;
  68. int x, y;
  69. RGBA* image;
  70. unsigned char* indimg;
  71. int delay;
  72. int identical;
  73. unsigned short objid;
  74. unsigned short cloneid;
  75. int clone;
  76. unsigned short precloneid;
  77. int preclone;
  78. struct _file_info* next;
  79. } file_info;
  80. void file_info_free ();
  81. // MNG callbacks
  82. mng_ptr MNG_DECL mng_alloc (mng_size_t iLen);
  83. void MNG_DECL mng_free (mng_ptr pPtr, mng_size_t iLen);
  84. mng_bool MNG_DECL mng_open_stream(mng_handle mng);
  85. mng_bool MNG_DECL mng_close_stream(mng_handle mng);
  86. mng_bool MNG_DECL mng_read_stream(mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes);
  87. mng_bool MNG_DECL mng_write_stream (mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes);
  88. mng_bool MNG_DECL mng_process_header(mng_handle mng, mng_uint32 width, mng_uint32 height);
  89. mng_ptr MNG_DECL mng_get_canvasline_read(mng_handle mng, mng_uint32 line);
  90. mng_ptr MNG_DECL mng_get_canvasline_write(mng_handle mng, mng_uint32 line);
  91. mng_bool MNG_DECL mng_refresh_display(mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h);
  92. mng_uint32 MNG_DECL mng_get_tickcount(mng_handle mng);
  93. mng_bool MNG_DECL mng_set_timer(mng_handle mng, mng_uint32 msecs);
  94. #define MAX_COLORS 0x100
  95. // global png/mng data
  96. char** Files = 0;
  97. int cFiles = 0;
  98. file_info* Infos = 0;
  99. mng_palette8 Pal;
  100. int cPalClr = 0;
  101. int mngw = 0, mngh = 0;
  102. int iback = 0;
  103. int timerate = 100; // default 100 ticks per second
  104. int framedelay = 20; // default 5 fps
  105. int _curframe = -1;
  106. int _curdeltaframe = -1;
  107. int
  108. main(int argc, char* argv[])
  109. {
  110. int ret = 0;
  111. parse_arguments (argc, argv, &opts);
  112. if (opts.framerate) // update delay
  113. framedelay = timerate / opts.framerate;
  114. if (!opts.inpath && (strchr (opts.inmask, '/') || strchr (opts.inmask, '\\')))
  115. {
  116. char *pch1, *pch2;
  117. opts.inpath = (char*) calloc (strlen (opts.inmask), 1);
  118. if (!opts.inpath)
  119. return error (EXIT_FAILURE, "No memory", 0);
  120. strcpy (opts.inpath, opts.inmask);
  121. pch1 = strrchr (opts.inpath, '/');
  122. pch2 = strrchr (opts.inpath, '\\');
  123. if (pch2 > pch1)
  124. pch1 = pch2;
  125. pch1[1] = 0; // term the path
  126. verbose ("Frame files in dir: %s\n", opts.inpath);
  127. }
  128. if (!opts.outfile)
  129. {
  130. char* pch;
  131. opts.outfile = (char*) calloc (strlen(opts.inmask) + 6, 1);
  132. if (!opts.outfile)
  133. return error (EXIT_FAILURE, "No memory", 0);
  134. strcpy (opts.outfile, opts.inmask);
  135. while ((pch = strchr (opts.outfile, '*')) != 0)
  136. strcpy (pch, pch + 1);
  137. pch = strstr (opts.outfile, ".png");
  138. if (!pch)
  139. pch = opts.outfile + strlen (opts.outfile);
  140. strcpy (pch, ".mng");
  141. if (pch == opts.outfile || pch[-1] == '/' || pch[-1] == '\\')
  142. { // have to fix blank name
  143. memmove (pch + 1, pch, strlen(pch) + 1);
  144. *pch = '1';
  145. }
  146. verbose ("Output file: %s\n", opts.outfile);
  147. }
  148. fprintf (stderr, "using timerate of %d and framedelay of %d\n", timerate, framedelay);
  149. ret = read_file_list ();
  150. if (!ret)
  151. {
  152. calc_mng_dims ();
  153. if (opts.backimage)
  154. select_back_image ();
  155. delta_images ();
  156. ret = write_mng_file ();
  157. }
  158. file_info_free ();
  159. free_file_list (Files);
  160. return ret;
  161. }
  162. int
  163. error(int errn, const char* fmt, const char* s)
  164. {
  165. fprintf(stderr, fmt, s);
  166. return errn;
  167. }
  168. void
  169. verbose(const char* fmt, const char* s)
  170. {
  171. if (!opts.verbose)
  172. return;
  173. fprintf(stderr, fmt, s);
  174. }
  175. void verbose_d (const char* fmt, int val)
  176. {
  177. if (!opts.verbose)
  178. return;
  179. fprintf(stderr, fmt, val);
  180. }
  181. void
  182. usage()
  183. {
  184. fprintf(stderr, "usage: makemng [-v] [-f rate] [-r] [-s size] [-o outputfile] <png-in-mask>\n");
  185. fprintf(stderr, "produces an MNG animation from a bunch of frame images\n");
  186. fprintf(stderr, "options:\n");
  187. fprintf(stderr, " -v\t\t : be verbose, explains things no human should know\n");
  188. fprintf(stderr, " -f rate\t : sets the framerate; rate is 1..100 per second (default 5)\n");
  189. fprintf(stderr, " -b\t\t : auto-select background frame (instead of frame0)\n");
  190. fprintf(stderr, " -r\t\t : split delta frames into full rectangles only\n");
  191. fprintf(stderr, " -s size\t : enable sector cleanup and set sector size (8..64)\n");
  192. fprintf(stderr, "diagnostical options:\n");
  193. fprintf(stderr, " -d\t\t : generate delta-mask PNGs (form: mask_FRM1_FRM2.png)\n");
  194. }
  195. void
  196. parse_arguments(int argc, char *argv[], struct options *opts)
  197. {
  198. char ch;
  199. memset(opts, '\0', sizeof (struct options));
  200. while ((ch = getopt(argc, argv, "?hvbdrf:s:o:")) != -1)
  201. {
  202. switch(ch)
  203. {
  204. case 'o':
  205. opts->outfile = optarg;
  206. break;
  207. case 'f':
  208. opts->framerate = atoi(optarg);
  209. if (opts->framerate < 1 || opts->framerate > 100)
  210. {
  211. fprintf(stderr, "invalid -f option value\n");
  212. usage();
  213. exit(EXIT_FAILURE);
  214. }
  215. break;
  216. case 'd':
  217. opts->deltamask = 1;
  218. break;
  219. case 'b':
  220. opts->backimage = 1;
  221. break;
  222. case 'r':
  223. opts->fullrects = 1;
  224. break;
  225. case 's':
  226. opts->sectorsize = atoi(optarg);
  227. if (opts->sectorsize < 8 || opts->sectorsize > 64)
  228. {
  229. fprintf(stderr, "invalid -r option value\n");
  230. usage();
  231. exit(EXIT_FAILURE);
  232. }
  233. break;
  234. case 'v':
  235. opts->verbose = 1;
  236. break;
  237. case '?':
  238. case 'h':
  239. default:
  240. usage();
  241. exit(EXIT_FAILURE);
  242. }
  243. }
  244. argc -= optind;
  245. argv += optind;
  246. if (argc != 1)
  247. {
  248. usage();
  249. exit(EXIT_FAILURE);
  250. }
  251. opts->inmask = argv[0];
  252. }
  253. void
  254. make_file_name(int index, char* buf)
  255. {
  256. if (opts.inpath)
  257. strcpy(buf, opts.inpath);
  258. else
  259. *buf = 0;
  260. strcat(buf, Files[index]);
  261. }
  262. void
  263. file_info_init (file_info* ms)
  264. {
  265. memset(ms, 0, sizeof(*ms));
  266. ms->identical = -1;
  267. ms->clone = -1;
  268. ms->preclone = -1;
  269. }
  270. void
  271. file_info_cleanup (file_info* ms)
  272. {
  273. file_info* fi = ms;
  274. while (fi)
  275. {
  276. file_info* tempi = fi;
  277. if (fi->image)
  278. {
  279. free(fi->image);
  280. fi->image = 0;
  281. }
  282. if (fi->indimg)
  283. {
  284. free(fi->indimg);
  285. fi->indimg = 0;
  286. }
  287. if (fi->f)
  288. {
  289. fclose(fi->f);
  290. fi->f = 0;
  291. }
  292. fi = fi->next;
  293. if (tempi != ms)
  294. free (tempi);
  295. }
  296. }
  297. void
  298. file_info_free ()
  299. {
  300. int i;
  301. if (Infos)
  302. {
  303. for (i = 0; i < cFiles; i++)
  304. file_info_cleanup (Infos + i);
  305. free (Infos);
  306. Infos = 0;
  307. }
  308. }
  309. int
  310. equal_colors (RGBA rgba, mng_palette8e mng_clr)
  311. {
  312. return rgba.bchan.r == mng_clr.iRed &&
  313. rgba.bchan.g == mng_clr.iGreen &&
  314. rgba.bchan.b == mng_clr.iBlue;
  315. }
  316. int
  317. lookup_palette (RGBA rgba)
  318. {
  319. int i;
  320. for (i = 0; i < cPalClr && !equal_colors(rgba, Pal[i]); i++)
  321. ;
  322. return i < cPalClr ? i : -1;
  323. }
  324. int
  325. update_palette (file_info* ms)
  326. {
  327. int i;
  328. for (i = 0; i < ms->w * ms->h; i++)
  329. {
  330. RGBA rgba = ms->image[i];
  331. int ipal = lookup_palette (rgba);
  332. if (ipal == -1)
  333. { // add color
  334. if (cPalClr >= MAX_COLORS)
  335. return 1;
  336. Pal[cPalClr].iRed = rgba.bchan.r;
  337. Pal[cPalClr].iGreen = rgba.bchan.g;
  338. Pal[cPalClr].iBlue = rgba.bchan.b;
  339. cPalClr++;
  340. }
  341. }
  342. return 0;
  343. }
  344. int
  345. convert_image_indexed (file_info* ms)
  346. {
  347. int i;
  348. ms->indimg = (unsigned char*) malloc (ms->w * ms->h);
  349. if (!ms->indimg)
  350. return 230;
  351. for (i = 0; i < ms->w * ms->h; i++)
  352. {
  353. int ipal = lookup_palette (ms->image[i]);
  354. if (ipal == -1)
  355. return 1; // something is screwed
  356. ms->indimg[i] = ipal;
  357. }
  358. free (ms->image);
  359. ms->image = 0;
  360. return 0;
  361. }
  362. int
  363. read_file_list (void)
  364. {
  365. int ret = 0;
  366. mng_handle mng;
  367. char namebuf[260];
  368. int i;
  369. cFiles = 0;
  370. Files = make_file_list(opts.inmask, &cFiles);
  371. if (!Files || cFiles == 0)
  372. {
  373. fprintf (stderr, "No frame files found\n");
  374. return 1;
  375. }
  376. Infos = (file_info*) malloc (sizeof(file_info) * cFiles);
  377. if (!Infos)
  378. return 251;
  379. memset(Infos, 0, sizeof(file_info) * cFiles);
  380. mng = mng_initialize (MNG_NULL, mng_alloc, mng_free, MNG_NULL);
  381. if (mng == MNG_NULL)
  382. return 250;
  383. // set the callbacks
  384. mng_setcb_openstream(mng, mng_open_stream);
  385. mng_setcb_closestream(mng, mng_close_stream);
  386. mng_setcb_readdata(mng, mng_read_stream);
  387. mng_setcb_processheader(mng, mng_process_header);
  388. mng_setcb_getcanvasline(mng, mng_get_canvasline_read);
  389. mng_setcb_gettickcount(mng, mng_get_tickcount);
  390. mng_setcb_settimer(mng, mng_set_timer);
  391. mng_setcb_refresh(mng, mng_refresh_display);
  392. for (i = 0; i < cFiles && !ret; i++)
  393. {
  394. file_info* rf = Infos + i;
  395. file_info_init (rf);
  396. make_file_name (i, namebuf);
  397. rf->fname = namebuf;
  398. rf->fmode = "rb";
  399. verbose_d ("%03d ", i); verbose ("reading '%s'...", rf->fname);
  400. mng_reset (mng);
  401. mng_set_userdata (mng, rf);
  402. for (ret = mng_readdisplay (mng);
  403. ret == MNG_NEEDMOREDATA || ret == MNG_NEEDTIMERWAIT;
  404. ret = mng_display_resume (mng))
  405. {
  406. if (ret == MNG_NEEDTIMERWAIT)
  407. rf->delay = 0;
  408. }
  409. if (ret)
  410. {
  411. fprintf (stderr, "Could not read '%s'\n", rf->fname);
  412. ret = 2;
  413. }
  414. ret = update_palette (rf);
  415. if (ret)
  416. {
  417. fprintf (stderr, "Too many unique colors (%d processed), giving up\n", i);
  418. ret = 3;
  419. }
  420. ret = convert_image_indexed (rf);
  421. if (ret)
  422. {
  423. fprintf (stderr, "Image conversion failed on '%s'\n", rf->fname);
  424. ret = 4;
  425. }
  426. verbose (" done\n", 0);
  427. }
  428. mng_cleanup (&mng);
  429. if (ret == MNG_NOERROR)
  430. fprintf (stderr, "%d animation frames\n", cFiles);
  431. return ret;
  432. }
  433. void
  434. calc_mng_dims (void)
  435. {
  436. int i;
  437. mngw = mngh = -1;
  438. // get max dims
  439. for (i = 0; i < cFiles; i++)
  440. {
  441. if (Infos[i].w > mngw)
  442. mngw = Infos[i].w;
  443. if (Infos[i].h > mngh)
  444. mngh = Infos[i].h;
  445. }
  446. // adjust images - center
  447. for (i = 0; i < cFiles; i++)
  448. {
  449. if (Infos[i].w < mngw)
  450. Infos[i].x = (mngw - Infos[i].w) >> 1;
  451. if (Infos[i].h < mngh)
  452. Infos[i].y = (mngh - Infos[i].h) >> 1;
  453. }
  454. }
  455. int
  456. compare_images (file_info* i1, file_info* i2)
  457. {
  458. int cnt = 0;
  459. int w, h, x, y;
  460. int dx1, dx2, dy1, dy2;
  461. if (i1->w > i2->w)
  462. {
  463. w = i2->w;
  464. dx1 = i2->x;
  465. dx2 = 0;
  466. }
  467. else if (i1->w < i2->w)
  468. {
  469. w = i1->w;
  470. dx1 = 0;
  471. dx2 = i1->x;
  472. }
  473. else
  474. {
  475. w = i1->w;
  476. dx1 = dx2 = 0;
  477. }
  478. if (i1->h > i2->h)
  479. {
  480. h = i2->h;
  481. dy1 = i2->y;
  482. dy2 = 0;
  483. }
  484. else if (i1->h < i2->h)
  485. {
  486. h = i1->h;
  487. dy1 = 0;
  488. dy2 = i1->y;
  489. }
  490. else
  491. {
  492. h = i1->h;
  493. dy1 = dy2 = 0;
  494. }
  495. for (y = 0; y < h; y++)
  496. {
  497. for (x = 0; x < w; x++)
  498. {
  499. if (i1->indimg[(y + dy1) * i1->w + x + dx1] != i2->indimg[(y + dy2) * i2->w + x + dx2])
  500. cnt++;
  501. }
  502. }
  503. return cnt;
  504. }
  505. void
  506. select_back_image (void)
  507. {
  508. int i;
  509. int* cdiff;
  510. int max;
  511. cdiff = (int*) calloc (cFiles, sizeof(int));
  512. if (!cdiff)
  513. return;
  514. verbose ("selecting optimal background image...", 0);
  515. for (i = 2; i < cFiles; i++)
  516. {
  517. if (Infos[i].w == mngw && Infos[i].h == mngh)
  518. {
  519. cdiff[i] = compare_images (Infos + i, Infos + i - 1) -
  520. compare_images (Infos + i, Infos + 0);
  521. }
  522. else
  523. {
  524. // image is smaller than animation and cannot be background
  525. cdiff[i] = 0x7fffffff;
  526. }
  527. }
  528. // the difference has to be big enough
  529. // or it will be useless
  530. iback = 0;
  531. max = mngw * mngh / 32;
  532. for (i = 2; i < cFiles; i++)
  533. {
  534. if (cdiff[i] > max)
  535. {
  536. iback = i;
  537. max = cdiff[i];
  538. }
  539. }
  540. verbose (" done\n", 0);
  541. fprintf(stderr, "frame %03d selected as background\n", iback);
  542. }
  543. int
  544. equal_images (int i1, int i2)
  545. {
  546. // deference identical chain
  547. while (Infos[i1].identical != -1)
  548. i1 = Infos[i1].identical;
  549. while (Infos[i2].identical != -1)
  550. i2 = Infos[i2].identical;
  551. if (i1 == i2)
  552. return 1;
  553. if (Infos[i1].x != Infos[i2].x || Infos[i1].y != Infos[i2].y
  554. || Infos[i1].w != Infos[i2].w || Infos[i1].h != Infos[i2].h)
  555. return 0;
  556. return compare_images (Infos + i1, Infos + i2) == 0;
  557. }
  558. int
  559. delta_adjust_positions (int* pos1, int* pos2)
  560. {
  561. if (*pos1 <= *pos2)
  562. return 1;
  563. else
  564. {
  565. (*pos1)--;
  566. (*pos2)--;
  567. return -1;
  568. }
  569. }
  570. void
  571. clean_expansion_horz (unsigned char* mask, int w, int x1, int y1, int x2, int y2, int threshold)
  572. {
  573. int x, y, dx, dy;
  574. // assume anything out of bounds is cleared
  575. int prevclear = threshold + 1;
  576. dy = delta_adjust_positions (&y1, &y2);
  577. dx = delta_adjust_positions (&x1, &x2);
  578. for (y = y1; y != y2; y += dy)
  579. {
  580. int dcnt, ecnt;
  581. dcnt = ecnt = 0;
  582. for (x = x1; x != x2; x += dx)
  583. {
  584. if (mask[y * w + x] == 1)
  585. dcnt++;
  586. else if (mask[y * w + x] == 2 || mask[y * w + x] == 3)
  587. ecnt++;
  588. }
  589. if (dcnt == 0 && ecnt == 0)
  590. { // line is clear
  591. prevclear++;
  592. }
  593. else if (dcnt == 0)
  594. {
  595. if (prevclear >= threshold)
  596. { // it's not clear yet, but it will be in a moment ;)
  597. int lx, ly = y;
  598. if (prevclear == threshold)
  599. { // need to clean everything we just checked
  600. ly = y - prevclear * dy;
  601. }
  602. for (ly = ly; ly != y + dy; ly += dy)
  603. for (lx = x1; lx != x2; lx += dx)
  604. mask[ly * w + lx] = 0;
  605. }
  606. prevclear++;
  607. }
  608. else
  609. { // line is dirty
  610. prevclear = 0;
  611. }
  612. }
  613. }
  614. void
  615. clean_expansion_vert (unsigned char* mask, int w, int x1, int y1, int x2, int y2, int threshold)
  616. {
  617. int x, y, dx, dy;
  618. // assume anything out of bounds is cleared
  619. int prevclear = threshold + 1;
  620. dy = delta_adjust_positions (&y1, &y2);
  621. dx = delta_adjust_positions (&x1, &x2);
  622. for (x = x1; x != x2; x += dx)
  623. {
  624. int dcnt, ecnt;
  625. dcnt = ecnt = 0;
  626. for (y = y1; y != y2; y += dy)
  627. {
  628. if (mask[y * w + x] == 1)
  629. dcnt++;
  630. else if (mask[y * w + x] == 2 || mask[y * w + x] == 3)
  631. ecnt++;
  632. }
  633. if (dcnt == 0 && ecnt == 0)
  634. { // line is clear
  635. prevclear++;
  636. }
  637. else if (dcnt == 0)
  638. {
  639. if (prevclear >= threshold)
  640. { // it's not clear yet, but it will be in a moment ;)
  641. int ly, lx = x;
  642. if (prevclear == threshold)
  643. { // need to clean everything we just checked
  644. lx = x - prevclear * dx;
  645. }
  646. for (lx = lx; lx != x + dx; lx += dx)
  647. for (ly = y1; ly != y2; ly += dy)
  648. mask[ly * w + lx] = 0;
  649. }
  650. prevclear++;
  651. }
  652. else
  653. { // line is dirty
  654. prevclear = 0;
  655. }
  656. }
  657. }
  658. struct _expand_corner
  659. {
  660. int x, y;
  661. int tx1, ty1;
  662. int tx2, ty2;
  663. }
  664. const expand_corner [] =
  665. {
  666. {0, 0, 1, 0, 0, 1}, // top-left
  667. {1, 0, 0, 0, 0, 1}, // top-mid, from left
  668. {1, 0, 2, 0, 2, 1}, // top-mid, from right
  669. {2, 0, 1, 0, 2, 1}, // top-right
  670. {0, 1, 0, 0, 1, 0}, // mid-left, from top
  671. {0, 1, 0, 2, 1, 2}, // mid-left, from bottom
  672. {2, 1, 1, 0, 2, 0}, // mid-right, from top
  673. {2, 1, 1, 2, 2, 2}, // mid-right, from bottom
  674. {0, 2, 1, 2, 0, 1}, // bot-left
  675. {1, 2, 0, 1, 0, 2}, // bot-mid, from left
  676. {1, 2, 2, 1, 2, 2}, // bot-mid, from right
  677. {2, 2, 1, 2, 2, 1}, // bot-right
  678. {-1,-1, -1,-1, -1,-1} // term
  679. };
  680. // this will recursively expand the missing corner pixels
  681. // recursion is limited so we dont overflow the stack
  682. int
  683. expand_rect (char* mask, int x, int y, int w, int h)
  684. {
  685. static int level = 0;
  686. int x1, y1, x2, y2, i, lx, ly;
  687. const struct _expand_corner* pc;
  688. signed char matrix[3][3];
  689. int cnt = 0;
  690. if (level > 99)
  691. return 1; // make sure parent knows it failed
  692. level++;
  693. if (x > 0)
  694. x1 = x - 1;
  695. else
  696. {
  697. for (i = 0; i < 3; i++)
  698. matrix[0][i] = -1;
  699. x1 = x;
  700. }
  701. if (y > 0)
  702. y1 = y - 1;
  703. else
  704. {
  705. for (i = 0; i < 3; i++)
  706. matrix[i][0] = -1;
  707. y1 = y;
  708. }
  709. if (x + 1 < w)
  710. x2 = x + 2;
  711. else
  712. {
  713. for (i = 0; i < 3; i++)
  714. matrix[2][i] = -1;
  715. x2 = x + 1;
  716. }
  717. if (y + 1 < h)
  718. y2 = y + 2;
  719. else
  720. {
  721. for (i = 0; i < 3; i++)
  722. matrix[i][2] = -1;
  723. y2 = y + 1;
  724. }
  725. for (ly = y1; ly < y2; ly++)
  726. for (lx = x1; lx < x2; lx++)
  727. matrix[lx - x + 1][ly - y + 1] = mask[ly * w + lx];
  728. // check corner pixels
  729. for (pc = expand_corner; pc->x != -1; pc++)
  730. {
  731. if (matrix[pc->x][pc->y] == 0 && matrix[pc->tx1][pc->ty1] > 0 && matrix[pc->tx2][pc->ty2] > 0)
  732. { // corner pixel missing
  733. int ofs = (y - 1 + pc->y) * w + (x - 1 + pc->x);
  734. matrix[pc->x][pc->y] = 3;
  735. // but it may already be present in the mask (recursive)
  736. if (mask[ofs] == 0)
  737. {
  738. mask[ofs] = 3;
  739. cnt += 1 + expand_rect (mask, x - 1 + pc->x, y - 1 + pc->y, w, h);
  740. }
  741. }
  742. }
  743. level--;
  744. return cnt;
  745. }
  746. file_info*
  747. file_info_add_image (file_info* fi)
  748. {
  749. file_info* ni;
  750. ni = (file_info*) malloc (sizeof(file_info));
  751. if (!ni)
  752. return 0;
  753. file_info_init (ni);
  754. while (fi->next)
  755. fi = fi->next;
  756. return fi->next = ni;
  757. }
  758. int
  759. is_multi_delta_image (file_info* fi)
  760. {
  761. return fi && fi->next;
  762. }
  763. #define MASK_COLORS 4
  764. mng_palette8e mask_pal[MASK_COLORS] =
  765. {
  766. {0x00, 0x00, 0x00},
  767. {0xff, 0xff, 0xff},
  768. {0x00, 0xff, 0x00},
  769. {0x00, 0x00, 0xff}
  770. };
  771. void
  772. create_mask_png (char* mask, int w, int h)
  773. {
  774. int ret = 0;
  775. mng_handle mng;
  776. file_info wf;
  777. char fname[260];
  778. mng_ptr imgdata;
  779. unsigned char* tempdata;
  780. unsigned char* p;
  781. uLong srcLen;
  782. uLong dstLen;
  783. int i;
  784. file_info_init (&wf);
  785. sprintf(fname, "mask_%03d_%03d.png", _curframe, _curdeltaframe);
  786. wf.fname = fname;
  787. wf.fmode = "wb";
  788. // extra byte in front of each line for filter type
  789. srcLen = w * h + h;
  790. tempdata = (mng_ptr) malloc(srcLen);
  791. if (!tempdata)
  792. return;
  793. // maximum necessary space
  794. // deflated data can be 100.1% + 12 bytes in worst case
  795. dstLen = srcLen + srcLen / 100 + 20; // extra 8 for safety
  796. imgdata = (mng_ptr) malloc(dstLen);
  797. if (!imgdata)
  798. return;
  799. for (i = 0, p = tempdata; i < w * h; i++, p++)
  800. {
  801. if (i % w == 0)
  802. { // write filter byte
  803. *p++ = 0;
  804. }
  805. *p = mask[i];
  806. }
  807. if (Z_OK != compress2(imgdata, &dstLen, tempdata, srcLen, 9))
  808. return;
  809. free(tempdata);
  810. mng = mng_initialize (&wf, mng_alloc, mng_free, MNG_NULL);
  811. if (mng == MNG_NULL)
  812. return;
  813. // set the callbacks
  814. mng_setcb_openstream(mng, mng_open_stream);
  815. mng_setcb_closestream(mng, mng_close_stream);
  816. mng_setcb_writedata(mng, mng_write_stream);
  817. ret = mng_create (mng);
  818. ret = mng_putchunk_ihdr (mng, w, h,
  819. MNG_BITDEPTH_8, MNG_COLORTYPE_INDEXED, MNG_COMPRESSION_DEFLATE,
  820. MNG_FILTER_ADAPTIVE, MNG_INTERLACE_NONE);
  821. if (ret == MNG_NOERROR)
  822. ret = mng_putchunk_plte (mng, 4, mask_pal);
  823. if (ret == MNG_NOERROR)
  824. ret = mng_putchunk_idat (mng, dstLen, imgdata);
  825. if (ret == MNG_NOERROR)
  826. ret = mng_putchunk_iend (mng);
  827. free (imgdata);
  828. if (ret == MNG_NOERROR)
  829. ret = mng_write (mng);
  830. mng_cleanup (&mng);
  831. file_info_cleanup (&wf);
  832. }
  833. int
  834. build_delta (file_info* i1, file_info* i2)
  835. {
  836. int w, h, x, y;
  837. int dx1, dx2, dy1, dy2;
  838. int cnt;
  839. char* mask = 0;
  840. if (i1->w > i2->w)
  841. {
  842. w = i2->w;
  843. dx1 = i2->x;
  844. dx2 = 0;
  845. }
  846. else if (i1->w < i2->w)
  847. {
  848. w = i1->w;
  849. dx1 = 0;
  850. dx2 = i1->x;
  851. }
  852. else
  853. {
  854. w = i1->w;
  855. dx1 = dx2 = 0;
  856. }
  857. if (i1->h > i2->h)
  858. {
  859. h = i2->h;
  860. dy1 = i2->y;
  861. dy2 = 0;
  862. }
  863. else if (i1->h < i2->h)
  864. {
  865. h = i1->h;
  866. dy1 = 0;
  867. dy2 = i1->y;
  868. }
  869. else
  870. {
  871. h = i1->h;
  872. dy1 = dy2 = 0;
  873. }
  874. mask = (char*) malloc (w * h);
  875. if (!mask)
  876. return 220;
  877. memset(mask, 0, w * h);
  878. // build diff mask first
  879. for (y = 0; y < h; y++)
  880. {
  881. for (x = 0; x < w; x++)
  882. {
  883. if (i1->indimg[(y + dy1) * i1->w + x + dx1] != i2->indimg[(y + dy2) * i2->w + x + dx2])
  884. // diff pixel
  885. mask[y * w + x] = 1;
  886. }
  887. }
  888. // coarse expand the diff pixels
  889. for (y = 0; y < h; y++)
  890. {
  891. for (x = 0; x < w; x++)
  892. {
  893. if (mask[y * w + x] == 1)
  894. {
  895. int x1 = x - 2;
  896. int x2 = x + 3;
  897. int y1 = y - 2;
  898. int y2 = y + 3;
  899. int lx;
  900. if (x1 < 0)
  901. x1 = 0;
  902. if (x2 > w)
  903. x2 = w;
  904. if (y1 < 0)
  905. y1 = 0;
  906. if (y2 > h)
  907. y2 = h;
  908. for (y1 = y1; y1 < y2; y1++)
  909. for (lx = x1; lx < x2; lx++)
  910. if (mask[y1 * w + lx] == 0)
  911. mask[y1 * w + lx] = 2;
  912. }
  913. }
  914. }
  915. // scan and remove extra expansion horizontally and vertically
  916. clean_expansion_vert (mask, w, 0, 0, w, h, 1);
  917. clean_expansion_vert (mask, w, w, 0, 0, h, 1);
  918. clean_expansion_horz (mask, w, 0, 0, w, h, 1);
  919. clean_expansion_horz (mask, w, 0, h, w, 0, 1);
  920. do // coarse expand the diff pixels
  921. { // merge would-be diff rectangles in the process
  922. cnt = 0;
  923. for (y = 0; y < h; y++)
  924. {
  925. for (x = 0; x < w; x++)
  926. {
  927. if (mask[y * w + x] != 0)
  928. cnt += expand_rect (mask, x, y, w, h);
  929. }
  930. }
  931. // repeat is something was expanded
  932. } while (cnt != 0);
  933. // at this point we should have guaranteed non-overlapping
  934. // rectangles that cover all of the delta areas
  935. if (opts.sectorsize)
  936. { // final expansion cleanup
  937. for (y = 0; y < h; y += opts.sectorsize)
  938. {
  939. for (x = 0; x < w; x += opts.sectorsize)
  940. {
  941. int x2, y2;
  942. cnt = 0;
  943. for (y2 = y; y2 < y + opts.sectorsize && y2 < h; y2++)
  944. for (x2 = x; x2 < x + opts.sectorsize && x2 < w; x2++)
  945. if (mask[y2 * w + x2] == 1)
  946. cnt++;
  947. if (cnt > 0)
  948. continue; // dirty sector
  949. // clean up sector
  950. for (y2 = y; y2 < y + opts.sectorsize && y2 < h; y2++)
  951. for (x2 = x; x2 < x + opts.sectorsize && x2 < w; x2++)
  952. mask[y2 * w + x2] = 0;
  953. }
  954. }
  955. }
  956. // check how muany pixels have to be replaced
  957. for (x = 0, cnt = 0; x < w * h; x++)
  958. if (mask[x])
  959. cnt++;
  960. if (opts.deltamask)
  961. create_mask_png (mask, w, h);
  962. // generate delta images
  963. if (cnt != w * h)
  964. {
  965. int ofs;
  966. for (y = 0, ofs = 0; y < h; y++)
  967. {
  968. for (x = 0; x < w; x++, ofs++)
  969. {
  970. if (mask[ofs] != 0)
  971. { // copy masked rectangle into a new image
  972. // and clear the mask
  973. int i;
  974. int rw, rh;
  975. int x2, y2;
  976. unsigned char* src;
  977. unsigned char* dst;
  978. file_info* ni;
  979. ni = file_info_add_image (i1);
  980. if (!ni)
  981. {
  982. x = w;
  983. y = h;
  984. break;
  985. }
  986. // lookup delta rectangle
  987. for (i = x, src = mask + ofs; i < w && *src != 0; i++, src++)
  988. ;
  989. ni->w = rw = i - x;
  990. if (opts.fullrects)
  991. { // locate only complete rectangles
  992. y2 = y + 1;
  993. for (i = y + 1, src = mask + ofs; i < h && *src != 0; i++, src += w)
  994. {
  995. unsigned char* src2;
  996. y2 = i;
  997. for (x2 = x, src2 = src; x2 < x + rw && *src2 != 0; x2++, src2++)
  998. ;
  999. if (x2 < x + rw)
  1000. break;
  1001. }
  1002. }
  1003. else
  1004. { // any rectangles
  1005. for (y2 = y + 1, src = mask + ofs; y2 < h && *src != 0; y2++, src += w)
  1006. ;
  1007. }
  1008. ni->h = rh = y2 - y;
  1009. ni->indimg = (unsigned char*) malloc (rw * rh);
  1010. if (!ni->indimg)
  1011. {
  1012. x = w;
  1013. y = h;
  1014. break;
  1015. }
  1016. // copy the pixels
  1017. for (i = 0, src = i1->indimg + (dy1 + y) * i1->w + dx1 + x, dst = ni->indimg;
  1018. i < rh;
  1019. i++, src += i1->w, dst += rw
  1020. )
  1021. {
  1022. memcpy (dst, src, rw);
  1023. memset (mask + ofs + i * w, 0, rw);
  1024. }
  1025. ni->x = i1->x + dx1 + x;
  1026. ni->y = i1->y + dy1 + y;
  1027. }
  1028. }
  1029. }
  1030. if (i1->next)
  1031. { // dispose of the original
  1032. file_info* ni = i1->next;
  1033. free (i1->indimg);
  1034. i1->indimg = ni->indimg;
  1035. i1->x = ni->x;
  1036. i1->y = ni->y;
  1037. i1->w = ni->w;
  1038. i1->h = ni->h;
  1039. i1->next = ni->next;
  1040. free (ni);
  1041. }
  1042. }
  1043. else
  1044. { // break here
  1045. cnt = 1;
  1046. }
  1047. if (mask)
  1048. free (mask);
  1049. return 0;
  1050. }
  1051. void
  1052. delta_images (void)
  1053. {
  1054. int i;
  1055. unsigned short nextid = 0x101;
  1056. verbose ("calculating frame image deltas", 0);
  1057. Infos[iback].objid = nextid++;
  1058. if (iback != 0)
  1059. { // set the first frame objid different
  1060. // from back id
  1061. Infos[0].objid = nextid++;
  1062. }
  1063. // remove dupes
  1064. for (i = 1; i < cFiles; i++)
  1065. {
  1066. int i2;
  1067. if (i == iback)
  1068. continue;
  1069. Infos[i].objid = Infos[i - 1].objid;
  1070. for (i2 = i - 1; i2 >= 0 && Infos[i].identical == -1; i2--)
  1071. {
  1072. int orgi2 = i2;
  1073. // deference identical chain
  1074. while (Infos[i2].identical != -1)
  1075. i2 = Infos[i2].identical;
  1076. if (equal_images (i, i2))
  1077. {
  1078. Infos[i].identical = i2;
  1079. // dont need image data anymore
  1080. if (Infos[i].indimg)
  1081. {
  1082. free (Infos[i].indimg);
  1083. Infos[i].indimg = 0;
  1084. }
  1085. if (orgi2 != i - 1)
  1086. { // detached descendant
  1087. // clone the object for it
  1088. if (Infos[i2].clone == -1)
  1089. { // no clones yet
  1090. Infos[i2].cloneid = nextid++;
  1091. Infos[i2].clone = i;
  1092. Infos[i].objid = Infos[i2].cloneid;
  1093. }
  1094. else
  1095. { // already cloned for another frame
  1096. // tell the frame to preclone it for
  1097. // this frame too
  1098. // dereference preclone chain first
  1099. for (i2 = Infos[i2].clone; Infos[i2].preclone != -1; i2 = Infos[i2].preclone)
  1100. ;
  1101. Infos[i2].preclone = i;
  1102. Infos[i2].precloneid = nextid++;
  1103. Infos[i].objid = Infos[i2].precloneid;
  1104. }
  1105. }
  1106. }
  1107. }
  1108. verbose (".", 0);
  1109. }
  1110. verbose ("|", 0);
  1111. // compute deltas
  1112. for (i = cFiles - 1; i >= 0; i--)
  1113. {
  1114. int i2;
  1115. if (i == iback || Infos[i].identical != -1)
  1116. // no delta needed
  1117. continue;
  1118. else
  1119. {
  1120. if (i == 0 && i != iback)
  1121. { // delta against original background
  1122. i2 = iback;
  1123. }
  1124. else
  1125. { // deref indentical chain
  1126. for (i2 = i - 1; i2 >= 0 && Infos[i2].identical != -1; i2 = Infos[i2].identical)
  1127. ;
  1128. // sanity check
  1129. if (Infos[i2].objid != Infos[i].objid)
  1130. {
  1131. fprintf (stderr, "delta_images: logical error 1\n");
  1132. exit(EXIT_FAILURE);
  1133. }
  1134. }
  1135. // debug info
  1136. _curframe = i;
  1137. _curdeltaframe = i2;
  1138. build_delta (Infos + i, Infos + i2);
  1139. }
  1140. verbose (".", 0);
  1141. }
  1142. verbose ("\n", 0);
  1143. }
  1144. int
  1145. get_png_image_data (file_info* ms, unsigned char* imgdata)
  1146. {
  1147. int i;
  1148. for (i = 0; i < ms->w * ms->h; i++, imgdata++)
  1149. {
  1150. if (i % ms->w == 0)
  1151. { // write filter byte
  1152. *imgdata++ = 0;
  1153. }
  1154. *imgdata = ms->indimg[i];
  1155. }
  1156. return 0;
  1157. }
  1158. int
  1159. compress_png (file_info* ms, mng_ptr imgdata, mng_uint32 imglen, mng_uint32p bytes)
  1160. {
  1161. int ret = 0;
  1162. unsigned char* tempdata;
  1163. uLong srcLen;
  1164. *bytes = 0;
  1165. // extra byte in front of each line for filter type
  1166. srcLen = ms->w * ms->h + ms->h;
  1167. tempdata = (mng_ptr) malloc(srcLen);
  1168. if (!tempdata)
  1169. return 241;
  1170. ret = get_png_image_data (ms, tempdata);
  1171. if (!ret)
  1172. {
  1173. uLong dstLen = imglen;
  1174. if (Z_OK == compress2(imgdata, &dstLen, tempdata, srcLen, 9))
  1175. *bytes = dstLen;
  1176. else
  1177. ret = 253;
  1178. }
  1179. free(tempdata);
  1180. return ret;
  1181. }
  1182. int
  1183. output_png (mng_handle mng, file_info* rf, int delta)
  1184. {
  1185. int ret = 0;
  1186. mng_ptr imgdata;
  1187. mng_uint32 imglen;
  1188. mng_uint32 cbcomp;
  1189. unsigned short objid = rf->objid;
  1190. // maximum necessary space
  1191. // deflated data can be 100.1% + 12 bytes in worst case
  1192. imglen = mngw * mngh + mngh;
  1193. imglen += imglen / 100 + 20; // extra 8 for safety
  1194. imgdata = (mng_ptr) malloc(imglen);
  1195. if (!imgdata)
  1196. return 252;
  1197. do
  1198. {
  1199. if (delta)
  1200. { // output delta
  1201. ret = mng_putchunk_dhdr (mng, objid,
  1202. MNG_IMAGETYPE_PNG, MNG_DELTATYPE_BLOCKPIXELREPLACE,
  1203. rf->w, rf->h, rf->x, rf->y);
  1204. }
  1205. else
  1206. { // output image verbatim
  1207. ret = mng_putchunk_ihdr (mng, rf->w, rf->h,
  1208. MNG_BITDEPTH_8, MNG_COLORTYPE_INDEXED, MNG_COMPRESSION_DEFLATE,
  1209. MNG_FILTER_ADAPTIVE, MNG_INTERLACE_NONE);
  1210. if (ret == MNG_NOERROR)
  1211. { // write empty PLTE to use the global PLTE
  1212. ret = mng_putchunk_plte (mng, 0, Pal);
  1213. //ret = mng_putchunk_plte (mng, cPalClr, Pal); // enable to write plain PNG
  1214. }
  1215. }
  1216. if (ret == MNG_NOERROR)
  1217. ret = compress_png (rf, imgdata, imglen, &cbcomp);
  1218. if (ret == MNG_NOERROR)
  1219. ret = mng_putchunk_idat (mng, cbcomp, imgdata);
  1220. if (ret == MNG_NOERROR)
  1221. ret = mng_putchunk_iend (mng);
  1222. } while ((rf = rf->next) != 0 && ret == MNG_NOERROR);
  1223. free (imgdata);
  1224. return ret;
  1225. }
  1226. int
  1227. write_mng_file (void)
  1228. {
  1229. int ret = 0;
  1230. mng_handle mng;
  1231. file_info wf;
  1232. file_info rf;
  1233. file_info backf;
  1234. int i;
  1235. unsigned short lastobjid;
  1236. char curframemode, newframemode;
  1237. mng = mng_initialize (MNG_NULL, mng_alloc, mng_free, MNG_NULL);
  1238. if (mng == MNG_NULL)
  1239. {
  1240. fprintf (stderr, "libmng did not init properly\n");
  1241. return 250;
  1242. }
  1243. // set the callbacks
  1244. mng_setcb_openstream(mng, mng_open_stream);
  1245. mng_setcb_closestream(mng, mng_close_stream);
  1246. mng_setcb_writedata(mng, mng_write_stream);
  1247. file_info_init (&wf);
  1248. wf.fname = opts.outfile;
  1249. wf.fmode = "wb";
  1250. mng_set_userdata (mng, &wf);
  1251. ret = mng_create (mng);
  1252. if (ret != MNG_NOERROR)
  1253. fprintf (stderr, "Could not create '%s'\n", wf.fname);
  1254. else
  1255. verbose ("writing MNG file '%s'", wf.fname);
  1256. ret = mng_putchunk_mhdr (mng, mngw, mngh, timerate, 0, 0, 0,
  1257. MNG_SIMPLICITY_VALID | MNG_SIMPLICITY_SIMPLEFEATURES |
  1258. MNG_SIMPLICITY_COMPLEXFEATURES | MNG_SIMPLICITY_DELTAPNG | 0x240);
  1259. //ret = mng_putchunk_term (mng, MNG_TERMACTION_LASTFRAME, MNG_ITERACTION_LASTFRAME, 0, 0);
  1260. ret = mng_putchunk_plte (mng, cPalClr, Pal);
  1261. ret = mng_putchunk_back (mng, 0,0,0, 0, 0, MNG_BACKGROUNDIMAGE_NOTILE);
  1262. curframemode = MNG_FRAMINGMODE_1;
  1263. ret = mng_putchunk_fram (mng, MNG_FALSE, curframemode, 0,MNG_NULL,
  1264. MNG_CHANGEDELAY_DEFAULT, MNG_CHANGETIMOUT_NO, MNG_CHANGECLIPPING_NO, MNG_CHANGESYNCID_NO,
  1265. framedelay, 0,0,0,0,0,0, MNG_NULL,0);
  1266. // define the staring image/object
  1267. backf = Infos[iback];
  1268. ret = mng_putchunk_defi (mng, backf.objid, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE, MNG_FALSE, 0,0, MNG_FALSE, 0,0,0,0);
  1269. ret = output_png (mng, &backf, 0);
  1270. //ret = mng_putchunk_save (mng, MNG_TRUE, 0,0);
  1271. //ret = mng_putchunk_seek (mng, 5, "start");
  1272. if (iback != 0)
  1273. { // clone the starting object for the first frame
  1274. ret = mng_putchunk_clon (mng, backf.objid, Infos[0].objid,
  1275. MNG_FULL_CLONE, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE_ASPARENT,
  1276. MNG_FALSE, 0,0,0);
  1277. }
  1278. lastobjid = 0;
  1279. for (i = 0; i < cFiles && ret == MNG_NOERROR; i++)
  1280. {
  1281. rf = Infos[i];
  1282. if (rf.precloneid != 0)
  1283. { // pre-clone the object for another frame
  1284. ret = mng_putchunk_clon (mng, rf.objid, rf.precloneid,
  1285. MNG_FULL_CLONE, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE_ASPARENT,
  1286. MNG_FALSE, 0,0,0);
  1287. }
  1288. if (is_multi_delta_image (&rf))
  1289. // multi-delta png; frame mode: 0-delay for subframe
  1290. newframemode = MNG_FRAMINGMODE_2;
  1291. else
  1292. // frame mode: 1 image per frame
  1293. newframemode = MNG_FRAMINGMODE_1;
  1294. if (newframemode != curframemode)
  1295. { // change framing mode only
  1296. ret = mng_putchunk_fram (mng, MNG_FALSE, newframemode, 0,MNG_NULL,
  1297. MNG_CHANGEDELAY_NO, MNG_CHANGETIMOUT_NO, MNG_CHANGECLIPPING_NO, MNG_CHANGESYNCID_NO,
  1298. 0,0,0,0,0,0,0, MNG_NULL,0);
  1299. curframemode = newframemode;
  1300. }
  1301. else if (curframemode == MNG_FRAMINGMODE_2)
  1302. { // start new subframe
  1303. ret = mng_putchunk_fram (mng, MNG_TRUE, 0,0,MNG_NULL,0,0,0,0,0,0,0,0,0,0,0,0,0);
  1304. }
  1305. if (rf.indimg != 0 && i != iback)
  1306. { // display a delta png
  1307. ret = output_png (mng, &rf, 1);
  1308. }
  1309. if (rf.cloneid != 0)
  1310. { // post-clone the object for another frame
  1311. ret = mng_putchunk_clon (mng, rf.objid, rf.cloneid,
  1312. MNG_FULL_CLONE, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE_ASPARENT,
  1313. MNG_FALSE, 0,0,0);
  1314. }
  1315. if (rf.objid != lastobjid || rf.identical != -1)
  1316. { // show the object for this frame
  1317. ret = mng_putchunk_show (mng, MNG_FALSE, rf.objid, rf.objid, MNG_SHOWMODE_0);
  1318. lastobjid = rf.objid;
  1319. }
  1320. verbose (".", 0);
  1321. }
  1322. //ret = mng_putchunk_seek (mng, 3, "end");
  1323. ret = mng_putchunk_mend (mng);
  1324. ret = mng_write (mng);
  1325. file_info_cleanup (&wf);
  1326. mng_cleanup (&mng);
  1327. if (ret == MNG_NOERROR)
  1328. verbose ("finished.\n", 0);
  1329. else
  1330. fprintf (stderr, "Could not create MNG file\n");
  1331. return ret;
  1332. }
  1333. mng_ptr MNG_DECL
  1334. mng_alloc (mng_size_t iLen)
  1335. {
  1336. mng_ptr ptr;
  1337. if (iLen & 0x80000000)
  1338. return 0; // MNG error!
  1339. ptr = malloc (iLen);
  1340. if (ptr)
  1341. memset(ptr, 0, iLen);
  1342. return ptr;
  1343. }
  1344. void MNG_DECL
  1345. mng_free (mng_ptr pPtr, mng_size_t iLen)
  1346. {
  1347. if (iLen)
  1348. free (pPtr);
  1349. }
  1350. mng_bool MNG_DECL
  1351. mng_open_stream (mng_handle mng)
  1352. {
  1353. file_info* ms;
  1354. ms = (file_info*) mng_get_userdata (mng);
  1355. ms->f = fopen (ms->fname, ms->fmode);
  1356. if (!ms->f)
  1357. {
  1358. fprintf(stderr, "unable to open '%s'\n", ms->fname);
  1359. return MNG_FALSE;
  1360. }
  1361. return MNG_TRUE;
  1362. }
  1363. mng_bool MNG_DECL
  1364. mng_close_stream (mng_handle mng)
  1365. {
  1366. file_info* ms;
  1367. ms = (file_info*) mng_get_userdata (mng);
  1368. fclose(ms->f);
  1369. ms->f = NULL;
  1370. return MNG_TRUE;
  1371. }
  1372. mng_bool MNG_DECL
  1373. mng_read_stream (mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes)
  1374. {
  1375. file_info* ms;
  1376. ms = (file_info*) mng_get_userdata (mng);
  1377. *bytes = fread(buffer, 1, size, ms->f);
  1378. return MNG_TRUE;
  1379. }
  1380. mng_bool MNG_DECL
  1381. mng_write_stream (mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes)
  1382. {
  1383. file_info* ms;
  1384. ms = (file_info*) mng_get_userdata (mng);
  1385. *bytes = fwrite(buffer, 1, size, ms->f);
  1386. return MNG_TRUE;
  1387. }
  1388. mng_bool MNG_DECL
  1389. mng_process_header (mng_handle mng, mng_uint32 width, mng_uint32 height)
  1390. {
  1391. file_info* ms;
  1392. ms = (file_info*) mng_get_userdata (mng);
  1393. ms->w = width;
  1394. ms->h = height;
  1395. ms->image = (RGBA*) malloc(sizeof(RGBA) * width * height);
  1396. if (!ms->image)
  1397. return MNG_FALSE;
  1398. mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8);
  1399. return MNG_TRUE;
  1400. }
  1401. mng_ptr MNG_DECL
  1402. mng_get_canvasline_read (mng_handle mng, mng_uint32 line)
  1403. {
  1404. file_info* ms;
  1405. mng_ptr row;
  1406. ms = (file_info*) mng_get_userdata (mng);
  1407. row = ms->image + ms->w * line;
  1408. return row;
  1409. }
  1410. mng_ptr MNG_DECL
  1411. mng_get_canvasline_write (mng_handle mng, mng_uint32 line)
  1412. {
  1413. file_info* ms;
  1414. ms = (file_info*) mng_get_userdata (mng);
  1415. //if (!ms->rowdata)
  1416. // ms->rowdata = (unsigned char*) malloc (ms->w);
  1417. //if (!ms->rowdata)
  1418. // return MNG_NULL;
  1419. //make_pal_row (ms, line, ms->rowdata);
  1420. // satisfying compiler
  1421. line = 0;
  1422. return MNG_NULL;
  1423. }
  1424. mng_bool MNG_DECL
  1425. mng_refresh_display (mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h)
  1426. {
  1427. // not implemented
  1428. file_info* ms;
  1429. ms = (file_info*) mng_get_userdata (mng);
  1430. // satisfying compiler
  1431. x = y = w = h = 0;
  1432. return MNG_TRUE;
  1433. }
  1434. mng_uint32 MNG_DECL
  1435. mng_get_tickcount (mng_handle mng)
  1436. {
  1437. // not implemented
  1438. file_info* ms;
  1439. static int tick = 0;
  1440. ms = (file_info*) mng_get_userdata (mng);
  1441. return tick += 50;
  1442. }
  1443. mng_bool MNG_DECL
  1444. mng_set_timer (mng_handle mng, mng_uint32 msecs)
  1445. {
  1446. // not implemented
  1447. file_info* ms;
  1448. ms = (file_info*) mng_get_userdata (mng);
  1449. ms->delay = msecs;
  1450. return MNG_TRUE;
  1451. }