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