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