/Engine/lib/lmng/contrib/msvc/makemng/makemng.c
C | 1765 lines | 1368 code | 314 blank | 83 comment | 359 complexity | fc33358173f4552a3c710bb00bcc9f89 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, LGPL-3.0, GPL-2.0, LGPL-2.0, MIT, CC-BY-SA-3.0
- /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <string.h>
- #include <malloc.h>
- #include <zlib.h>
- #include <libmng.h>
- #ifdef WIN32
- # include <getopt.h>
- #else
- # include <unistd.h>
- #endif
- struct options
- {
- char *inmask;
- char *inpath;
- char *outfile;
- int framerate;
- char verbose;
- char backimage;
- char deltamask;
- int sectorsize;
- int fullrects;
- } opts;
- // externals
- char** make_file_list (const char* pattern, int* pnum_entries);
- void free_file_list (char** list);
- // internals
- int error (int errn, const char* fmt, const char* s);
- void verbose (const char* fmt, const char* s);
- void verbose_d (const char* fmt, int val);
- void parse_arguments (int argc, char *argv[], struct options *opts);
- int read_file_list (void);
- void calc_mng_dims (void);
- void select_back_image (void);
- void delta_images (void);
- int write_mng_file (void);
- typedef union
- {
- struct
- {
- unsigned char r, g, b, a;
- } bchan;
- unsigned char channels[4];
- unsigned int value;
- } RGBA;
- typedef struct _file_info
- {
- FILE* f;
- char* fname;
- char* fmode;
- int w, h;
- int x, y;
- RGBA* image;
- unsigned char* indimg;
- int delay;
- int identical;
- unsigned short objid;
- unsigned short cloneid;
- int clone;
- unsigned short precloneid;
- int preclone;
- struct _file_info* next;
- } file_info;
- void file_info_free ();
- // MNG callbacks
- mng_ptr MNG_DECL mng_alloc (mng_size_t iLen);
- void MNG_DECL mng_free (mng_ptr pPtr, mng_size_t iLen);
- mng_bool MNG_DECL mng_open_stream(mng_handle mng);
- mng_bool MNG_DECL mng_close_stream(mng_handle mng);
- mng_bool MNG_DECL mng_read_stream(mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes);
- mng_bool MNG_DECL mng_write_stream (mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes);
- mng_bool MNG_DECL mng_process_header(mng_handle mng, mng_uint32 width, mng_uint32 height);
- mng_ptr MNG_DECL mng_get_canvasline_read(mng_handle mng, mng_uint32 line);
- mng_ptr MNG_DECL mng_get_canvasline_write(mng_handle mng, mng_uint32 line);
- mng_bool MNG_DECL mng_refresh_display(mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h);
- mng_uint32 MNG_DECL mng_get_tickcount(mng_handle mng);
- mng_bool MNG_DECL mng_set_timer(mng_handle mng, mng_uint32 msecs);
- #define MAX_COLORS 0x100
- // global png/mng data
- char** Files = 0;
- int cFiles = 0;
- file_info* Infos = 0;
- mng_palette8 Pal;
- int cPalClr = 0;
- int mngw = 0, mngh = 0;
- int iback = 0;
- int timerate = 100; // default 100 ticks per second
- int framedelay = 20; // default 5 fps
- int _curframe = -1;
- int _curdeltaframe = -1;
- int
- main(int argc, char* argv[])
- {
- int ret = 0;
- parse_arguments (argc, argv, &opts);
- if (opts.framerate) // update delay
- framedelay = timerate / opts.framerate;
- if (!opts.inpath && (strchr (opts.inmask, '/') || strchr (opts.inmask, '\\')))
- {
- char *pch1, *pch2;
- opts.inpath = (char*) calloc (strlen (opts.inmask), 1);
- if (!opts.inpath)
- return error (EXIT_FAILURE, "No memory", 0);
- strcpy (opts.inpath, opts.inmask);
- pch1 = strrchr (opts.inpath, '/');
- pch2 = strrchr (opts.inpath, '\\');
- if (pch2 > pch1)
- pch1 = pch2;
- pch1[1] = 0; // term the path
- verbose ("Frame files in dir: %s\n", opts.inpath);
- }
- if (!opts.outfile)
- {
- char* pch;
- opts.outfile = (char*) calloc (strlen(opts.inmask) + 6, 1);
- if (!opts.outfile)
- return error (EXIT_FAILURE, "No memory", 0);
- strcpy (opts.outfile, opts.inmask);
- while ((pch = strchr (opts.outfile, '*')) != 0)
- strcpy (pch, pch + 1);
-
- pch = strstr (opts.outfile, ".png");
- if (!pch)
- pch = opts.outfile + strlen (opts.outfile);
- strcpy (pch, ".mng");
- if (pch == opts.outfile || pch[-1] == '/' || pch[-1] == '\\')
- { // have to fix blank name
- memmove (pch + 1, pch, strlen(pch) + 1);
- *pch = '1';
- }
- verbose ("Output file: %s\n", opts.outfile);
- }
- fprintf (stderr, "using timerate of %d and framedelay of %d\n", timerate, framedelay);
- ret = read_file_list ();
- if (!ret)
- {
- calc_mng_dims ();
- if (opts.backimage)
- select_back_image ();
- delta_images ();
- ret = write_mng_file ();
- }
- file_info_free ();
- free_file_list (Files);
- return ret;
- }
- int
- error(int errn, const char* fmt, const char* s)
- {
- fprintf(stderr, fmt, s);
- return errn;
- }
- void
- verbose(const char* fmt, const char* s)
- {
- if (!opts.verbose)
- return;
- fprintf(stderr, fmt, s);
- }
- void verbose_d (const char* fmt, int val)
- {
- if (!opts.verbose)
- return;
- fprintf(stderr, fmt, val);
- }
- void
- usage()
- {
- fprintf(stderr, "usage: makemng [-v] [-f rate] [-r] [-s size] [-o outputfile] <png-in-mask>\n");
- fprintf(stderr, "produces an MNG animation from a bunch of frame images\n");
- fprintf(stderr, "options:\n");
- fprintf(stderr, " -v\t\t : be verbose, explains things no human should know\n");
- fprintf(stderr, " -f rate\t : sets the framerate; rate is 1..100 per second (default 5)\n");
- fprintf(stderr, " -b\t\t : auto-select background frame (instead of frame0)\n");
- fprintf(stderr, " -r\t\t : split delta frames into full rectangles only\n");
- fprintf(stderr, " -s size\t : enable sector cleanup and set sector size (8..64)\n");
- fprintf(stderr, "diagnostical options:\n");
- fprintf(stderr, " -d\t\t : generate delta-mask PNGs (form: mask_FRM1_FRM2.png)\n");
- }
- void
- parse_arguments(int argc, char *argv[], struct options *opts)
- {
- char ch;
-
- memset(opts, '\0', sizeof (struct options));
- while ((ch = getopt(argc, argv, "?hvbdrf:s:o:")) != -1)
- {
- switch(ch)
- {
- case 'o':
- opts->outfile = optarg;
- break;
- case 'f':
- opts->framerate = atoi(optarg);
- if (opts->framerate < 1 || opts->framerate > 100)
- {
- fprintf(stderr, "invalid -f option value\n");
- usage();
- exit(EXIT_FAILURE);
- }
- break;
- case 'd':
- opts->deltamask = 1;
- break;
- case 'b':
- opts->backimage = 1;
- break;
- case 'r':
- opts->fullrects = 1;
- break;
- case 's':
- opts->sectorsize = atoi(optarg);
- if (opts->sectorsize < 8 || opts->sectorsize > 64)
- {
- fprintf(stderr, "invalid -r option value\n");
- usage();
- exit(EXIT_FAILURE);
- }
- break;
- case 'v':
- opts->verbose = 1;
- break;
- case '?':
- case 'h':
- default:
- usage();
- exit(EXIT_FAILURE);
- }
- }
- argc -= optind;
- argv += optind;
- if (argc != 1)
- {
- usage();
- exit(EXIT_FAILURE);
- }
-
- opts->inmask = argv[0];
- }
- void
- make_file_name(int index, char* buf)
- {
- if (opts.inpath)
- strcpy(buf, opts.inpath);
- else
- *buf = 0;
- strcat(buf, Files[index]);
- }
- void
- file_info_init (file_info* ms)
- {
- memset(ms, 0, sizeof(*ms));
- ms->identical = -1;
- ms->clone = -1;
- ms->preclone = -1;
- }
- void
- file_info_cleanup (file_info* ms)
- {
- file_info* fi = ms;
- while (fi)
- {
- file_info* tempi = fi;
- if (fi->image)
- {
- free(fi->image);
- fi->image = 0;
- }
- if (fi->indimg)
- {
- free(fi->indimg);
- fi->indimg = 0;
- }
- if (fi->f)
- {
- fclose(fi->f);
- fi->f = 0;
- }
-
- fi = fi->next;
- if (tempi != ms)
- free (tempi);
- }
- }
- void
- file_info_free ()
- {
- int i;
- if (Infos)
- {
- for (i = 0; i < cFiles; i++)
- file_info_cleanup (Infos + i);
- free (Infos);
- Infos = 0;
- }
- }
- int
- equal_colors (RGBA rgba, mng_palette8e mng_clr)
- {
- return rgba.bchan.r == mng_clr.iRed &&
- rgba.bchan.g == mng_clr.iGreen &&
- rgba.bchan.b == mng_clr.iBlue;
- }
- int
- lookup_palette (RGBA rgba)
- {
- int i;
- for (i = 0; i < cPalClr && !equal_colors(rgba, Pal[i]); i++)
- ;
-
- return i < cPalClr ? i : -1;
- }
- int
- update_palette (file_info* ms)
- {
- int i;
- for (i = 0; i < ms->w * ms->h; i++)
- {
- RGBA rgba = ms->image[i];
- int ipal = lookup_palette (rgba);
- if (ipal == -1)
- { // add color
- if (cPalClr >= MAX_COLORS)
- return 1;
- Pal[cPalClr].iRed = rgba.bchan.r;
- Pal[cPalClr].iGreen = rgba.bchan.g;
- Pal[cPalClr].iBlue = rgba.bchan.b;
- cPalClr++;
- }
- }
- return 0;
- }
- int
- convert_image_indexed (file_info* ms)
- {
- int i;
- ms->indimg = (unsigned char*) malloc (ms->w * ms->h);
- if (!ms->indimg)
- return 230;
- for (i = 0; i < ms->w * ms->h; i++)
- {
- int ipal = lookup_palette (ms->image[i]);
- if (ipal == -1)
- return 1; // something is screwed
- ms->indimg[i] = ipal;
- }
-
- free (ms->image);
- ms->image = 0;
- return 0;
- }
- int
- read_file_list (void)
- {
- int ret = 0;
- mng_handle mng;
- char namebuf[260];
- int i;
- cFiles = 0;
- Files = make_file_list(opts.inmask, &cFiles);
- if (!Files || cFiles == 0)
- {
- fprintf (stderr, "No frame files found\n");
- return 1;
- }
- Infos = (file_info*) malloc (sizeof(file_info) * cFiles);
- if (!Infos)
- return 251;
- memset(Infos, 0, sizeof(file_info) * cFiles);
- mng = mng_initialize (MNG_NULL, mng_alloc, mng_free, MNG_NULL);
- if (mng == MNG_NULL)
- return 250;
- // set the callbacks
- mng_setcb_openstream(mng, mng_open_stream);
- mng_setcb_closestream(mng, mng_close_stream);
- mng_setcb_readdata(mng, mng_read_stream);
- mng_setcb_processheader(mng, mng_process_header);
- mng_setcb_getcanvasline(mng, mng_get_canvasline_read);
- mng_setcb_gettickcount(mng, mng_get_tickcount);
- mng_setcb_settimer(mng, mng_set_timer);
- mng_setcb_refresh(mng, mng_refresh_display);
- for (i = 0; i < cFiles && !ret; i++)
- {
- file_info* rf = Infos + i;
- file_info_init (rf);
- make_file_name (i, namebuf);
- rf->fname = namebuf;
- rf->fmode = "rb";
- verbose_d ("%03d ", i); verbose ("reading '%s'...", rf->fname);
- mng_reset (mng);
- mng_set_userdata (mng, rf);
- for (ret = mng_readdisplay (mng);
- ret == MNG_NEEDMOREDATA || ret == MNG_NEEDTIMERWAIT;
- ret = mng_display_resume (mng))
- {
- if (ret == MNG_NEEDTIMERWAIT)
- rf->delay = 0;
- }
- if (ret)
- {
- fprintf (stderr, "Could not read '%s'\n", rf->fname);
- ret = 2;
- }
- ret = update_palette (rf);
- if (ret)
- {
- fprintf (stderr, "Too many unique colors (%d processed), giving up\n", i);
- ret = 3;
- }
- ret = convert_image_indexed (rf);
- if (ret)
- {
- fprintf (stderr, "Image conversion failed on '%s'\n", rf->fname);
- ret = 4;
- }
- verbose (" done\n", 0);
- }
- mng_cleanup (&mng);
- if (ret == MNG_NOERROR)
- fprintf (stderr, "%d animation frames\n", cFiles);
- return ret;
- }
- void
- calc_mng_dims (void)
- {
- int i;
- mngw = mngh = -1;
- // get max dims
- for (i = 0; i < cFiles; i++)
- {
- if (Infos[i].w > mngw)
- mngw = Infos[i].w;
-
- if (Infos[i].h > mngh)
- mngh = Infos[i].h;
- }
- // adjust images - center
- for (i = 0; i < cFiles; i++)
- {
- if (Infos[i].w < mngw)
- Infos[i].x = (mngw - Infos[i].w) >> 1;
-
- if (Infos[i].h < mngh)
- Infos[i].y = (mngh - Infos[i].h) >> 1;
- }
- }
- int
- compare_images (file_info* i1, file_info* i2)
- {
- int cnt = 0;
- int w, h, x, y;
- int dx1, dx2, dy1, dy2;
- if (i1->w > i2->w)
- {
- w = i2->w;
- dx1 = i2->x;
- dx2 = 0;
- }
- else if (i1->w < i2->w)
- {
- w = i1->w;
- dx1 = 0;
- dx2 = i1->x;
- }
- else
- {
- w = i1->w;
- dx1 = dx2 = 0;
- }
- if (i1->h > i2->h)
- {
- h = i2->h;
- dy1 = i2->y;
- dy2 = 0;
- }
- else if (i1->h < i2->h)
- {
- h = i1->h;
- dy1 = 0;
- dy2 = i1->y;
- }
- else
- {
- h = i1->h;
- dy1 = dy2 = 0;
- }
- for (y = 0; y < h; y++)
- {
- for (x = 0; x < w; x++)
- {
- if (i1->indimg[(y + dy1) * i1->w + x + dx1] != i2->indimg[(y + dy2) * i2->w + x + dx2])
- cnt++;
- }
- }
- return cnt;
- }
- void
- select_back_image (void)
- {
- int i;
- int* cdiff;
- int max;
- cdiff = (int*) calloc (cFiles, sizeof(int));
- if (!cdiff)
- return;
- verbose ("selecting optimal background image...", 0);
- for (i = 2; i < cFiles; i++)
- {
- if (Infos[i].w == mngw && Infos[i].h == mngh)
- {
- cdiff[i] = compare_images (Infos + i, Infos + i - 1) -
- compare_images (Infos + i, Infos + 0);
- }
- else
- {
- // image is smaller than animation and cannot be background
- cdiff[i] = 0x7fffffff;
- }
- }
- // the difference has to be big enough
- // or it will be useless
- iback = 0;
- max = mngw * mngh / 32;
- for (i = 2; i < cFiles; i++)
- {
- if (cdiff[i] > max)
- {
- iback = i;
- max = cdiff[i];
- }
- }
- verbose (" done\n", 0);
- fprintf(stderr, "frame %03d selected as background\n", iback);
- }
- int
- equal_images (int i1, int i2)
- {
- // deference identical chain
- while (Infos[i1].identical != -1)
- i1 = Infos[i1].identical;
- while (Infos[i2].identical != -1)
- i2 = Infos[i2].identical;
- if (i1 == i2)
- return 1;
- if (Infos[i1].x != Infos[i2].x || Infos[i1].y != Infos[i2].y
- || Infos[i1].w != Infos[i2].w || Infos[i1].h != Infos[i2].h)
- return 0;
- return compare_images (Infos + i1, Infos + i2) == 0;
- }
- int
- delta_adjust_positions (int* pos1, int* pos2)
- {
- if (*pos1 <= *pos2)
- return 1;
- else
- {
- (*pos1)--;
- (*pos2)--;
- return -1;
- }
- }
- void
- clean_expansion_horz (unsigned char* mask, int w, int x1, int y1, int x2, int y2, int threshold)
- {
- int x, y, dx, dy;
- // assume anything out of bounds is cleared
- int prevclear = threshold + 1;
- dy = delta_adjust_positions (&y1, &y2);
- dx = delta_adjust_positions (&x1, &x2);
- for (y = y1; y != y2; y += dy)
- {
- int dcnt, ecnt;
-
- dcnt = ecnt = 0;
- for (x = x1; x != x2; x += dx)
- {
- if (mask[y * w + x] == 1)
- dcnt++;
- else if (mask[y * w + x] == 2 || mask[y * w + x] == 3)
- ecnt++;
- }
-
- if (dcnt == 0 && ecnt == 0)
- { // line is clear
- prevclear++;
- }
- else if (dcnt == 0)
- {
- if (prevclear >= threshold)
- { // it's not clear yet, but it will be in a moment ;)
- int lx, ly = y;
-
- if (prevclear == threshold)
- { // need to clean everything we just checked
- ly = y - prevclear * dy;
- }
- for (ly = ly; ly != y + dy; ly += dy)
- for (lx = x1; lx != x2; lx += dx)
- mask[ly * w + lx] = 0;
- }
- prevclear++;
- }
- else
- { // line is dirty
- prevclear = 0;
- }
- }
- }
- void
- clean_expansion_vert (unsigned char* mask, int w, int x1, int y1, int x2, int y2, int threshold)
- {
- int x, y, dx, dy;
- // assume anything out of bounds is cleared
- int prevclear = threshold + 1;
- dy = delta_adjust_positions (&y1, &y2);
- dx = delta_adjust_positions (&x1, &x2);
- for (x = x1; x != x2; x += dx)
- {
- int dcnt, ecnt;
-
- dcnt = ecnt = 0;
- for (y = y1; y != y2; y += dy)
- {
- if (mask[y * w + x] == 1)
- dcnt++;
- else if (mask[y * w + x] == 2 || mask[y * w + x] == 3)
- ecnt++;
- }
-
- if (dcnt == 0 && ecnt == 0)
- { // line is clear
- prevclear++;
- }
- else if (dcnt == 0)
- {
- if (prevclear >= threshold)
- { // it's not clear yet, but it will be in a moment ;)
- int ly, lx = x;
- if (prevclear == threshold)
- { // need to clean everything we just checked
- lx = x - prevclear * dx;
- }
- for (lx = lx; lx != x + dx; lx += dx)
- for (ly = y1; ly != y2; ly += dy)
- mask[ly * w + lx] = 0;
- }
- prevclear++;
- }
- else
- { // line is dirty
- prevclear = 0;
- }
- }
- }
- struct _expand_corner
- {
- int x, y;
- int tx1, ty1;
- int tx2, ty2;
- }
- const expand_corner [] =
- {
- {0, 0, 1, 0, 0, 1}, // top-left
- {1, 0, 0, 0, 0, 1}, // top-mid, from left
- {1, 0, 2, 0, 2, 1}, // top-mid, from right
- {2, 0, 1, 0, 2, 1}, // top-right
- {0, 1, 0, 0, 1, 0}, // mid-left, from top
- {0, 1, 0, 2, 1, 2}, // mid-left, from bottom
- {2, 1, 1, 0, 2, 0}, // mid-right, from top
- {2, 1, 1, 2, 2, 2}, // mid-right, from bottom
- {0, 2, 1, 2, 0, 1}, // bot-left
- {1, 2, 0, 1, 0, 2}, // bot-mid, from left
- {1, 2, 2, 1, 2, 2}, // bot-mid, from right
- {2, 2, 1, 2, 2, 1}, // bot-right
- {-1,-1, -1,-1, -1,-1} // term
- };
- // this will recursively expand the missing corner pixels
- // recursion is limited so we dont overflow the stack
- int
- expand_rect (char* mask, int x, int y, int w, int h)
- {
- static int level = 0;
- int x1, y1, x2, y2, i, lx, ly;
- const struct _expand_corner* pc;
- signed char matrix[3][3];
- int cnt = 0;
- if (level > 99)
- return 1; // make sure parent knows it failed
- level++;
- if (x > 0)
- x1 = x - 1;
- else
- {
- for (i = 0; i < 3; i++)
- matrix[0][i] = -1;
-
- x1 = x;
- }
- if (y > 0)
- y1 = y - 1;
- else
- {
- for (i = 0; i < 3; i++)
- matrix[i][0] = -1;
-
- y1 = y;
- }
- if (x + 1 < w)
- x2 = x + 2;
- else
- {
- for (i = 0; i < 3; i++)
- matrix[2][i] = -1;
-
- x2 = x + 1;
- }
- if (y + 1 < h)
- y2 = y + 2;
- else
- {
- for (i = 0; i < 3; i++)
- matrix[i][2] = -1;
- y2 = y + 1;
- }
- for (ly = y1; ly < y2; ly++)
- for (lx = x1; lx < x2; lx++)
- matrix[lx - x + 1][ly - y + 1] = mask[ly * w + lx];
- // check corner pixels
- for (pc = expand_corner; pc->x != -1; pc++)
- {
- if (matrix[pc->x][pc->y] == 0 && matrix[pc->tx1][pc->ty1] > 0 && matrix[pc->tx2][pc->ty2] > 0)
- { // corner pixel missing
- int ofs = (y - 1 + pc->y) * w + (x - 1 + pc->x);
-
- matrix[pc->x][pc->y] = 3;
- // but it may already be present in the mask (recursive)
- if (mask[ofs] == 0)
- {
- mask[ofs] = 3;
- cnt += 1 + expand_rect (mask, x - 1 + pc->x, y - 1 + pc->y, w, h);
- }
- }
- }
- level--;
- return cnt;
- }
- file_info*
- file_info_add_image (file_info* fi)
- {
- file_info* ni;
- ni = (file_info*) malloc (sizeof(file_info));
- if (!ni)
- return 0;
- file_info_init (ni);
- while (fi->next)
- fi = fi->next;
- return fi->next = ni;
- }
- int
- is_multi_delta_image (file_info* fi)
- {
- return fi && fi->next;
- }
- #define MASK_COLORS 4
- mng_palette8e mask_pal[MASK_COLORS] =
- {
- {0x00, 0x00, 0x00},
- {0xff, 0xff, 0xff},
- {0x00, 0xff, 0x00},
- {0x00, 0x00, 0xff}
- };
- void
- create_mask_png (char* mask, int w, int h)
- {
- int ret = 0;
- mng_handle mng;
- file_info wf;
- char fname[260];
- mng_ptr imgdata;
- unsigned char* tempdata;
- unsigned char* p;
- uLong srcLen;
- uLong dstLen;
- int i;
- file_info_init (&wf);
- sprintf(fname, "mask_%03d_%03d.png", _curframe, _curdeltaframe);
- wf.fname = fname;
- wf.fmode = "wb";
- // extra byte in front of each line for filter type
- srcLen = w * h + h;
- tempdata = (mng_ptr) malloc(srcLen);
- if (!tempdata)
- return;
- // maximum necessary space
- // deflated data can be 100.1% + 12 bytes in worst case
- dstLen = srcLen + srcLen / 100 + 20; // extra 8 for safety
- imgdata = (mng_ptr) malloc(dstLen);
- if (!imgdata)
- return;
- for (i = 0, p = tempdata; i < w * h; i++, p++)
- {
- if (i % w == 0)
- { // write filter byte
- *p++ = 0;
- }
-
- *p = mask[i];
- }
- if (Z_OK != compress2(imgdata, &dstLen, tempdata, srcLen, 9))
- return;
- free(tempdata);
- mng = mng_initialize (&wf, mng_alloc, mng_free, MNG_NULL);
- if (mng == MNG_NULL)
- return;
- // set the callbacks
- mng_setcb_openstream(mng, mng_open_stream);
- mng_setcb_closestream(mng, mng_close_stream);
- mng_setcb_writedata(mng, mng_write_stream);
- ret = mng_create (mng);
- ret = mng_putchunk_ihdr (mng, w, h,
- MNG_BITDEPTH_8, MNG_COLORTYPE_INDEXED, MNG_COMPRESSION_DEFLATE,
- MNG_FILTER_ADAPTIVE, MNG_INTERLACE_NONE);
- if (ret == MNG_NOERROR)
- ret = mng_putchunk_plte (mng, 4, mask_pal);
- if (ret == MNG_NOERROR)
- ret = mng_putchunk_idat (mng, dstLen, imgdata);
- if (ret == MNG_NOERROR)
- ret = mng_putchunk_iend (mng);
- free (imgdata);
- if (ret == MNG_NOERROR)
- ret = mng_write (mng);
- mng_cleanup (&mng);
- file_info_cleanup (&wf);
- }
- int
- build_delta (file_info* i1, file_info* i2)
- {
- int w, h, x, y;
- int dx1, dx2, dy1, dy2;
- int cnt;
- char* mask = 0;
- if (i1->w > i2->w)
- {
- w = i2->w;
- dx1 = i2->x;
- dx2 = 0;
- }
- else if (i1->w < i2->w)
- {
- w = i1->w;
- dx1 = 0;
- dx2 = i1->x;
- }
- else
- {
- w = i1->w;
- dx1 = dx2 = 0;
- }
- if (i1->h > i2->h)
- {
- h = i2->h;
- dy1 = i2->y;
- dy2 = 0;
- }
- else if (i1->h < i2->h)
- {
- h = i1->h;
- dy1 = 0;
- dy2 = i1->y;
- }
- else
- {
- h = i1->h;
- dy1 = dy2 = 0;
- }
- mask = (char*) malloc (w * h);
- if (!mask)
- return 220;
- memset(mask, 0, w * h);
- // build diff mask first
- for (y = 0; y < h; y++)
- {
- for (x = 0; x < w; x++)
- {
- if (i1->indimg[(y + dy1) * i1->w + x + dx1] != i2->indimg[(y + dy2) * i2->w + x + dx2])
- // diff pixel
- mask[y * w + x] = 1;
- }
- }
- // coarse expand the diff pixels
- for (y = 0; y < h; y++)
- {
- for (x = 0; x < w; x++)
- {
- if (mask[y * w + x] == 1)
- {
- int x1 = x - 2;
- int x2 = x + 3;
- int y1 = y - 2;
- int y2 = y + 3;
- int lx;
- if (x1 < 0)
- x1 = 0;
- if (x2 > w)
- x2 = w;
- if (y1 < 0)
- y1 = 0;
- if (y2 > h)
- y2 = h;
- for (y1 = y1; y1 < y2; y1++)
- for (lx = x1; lx < x2; lx++)
- if (mask[y1 * w + lx] == 0)
- mask[y1 * w + lx] = 2;
- }
- }
- }
- // scan and remove extra expansion horizontally and vertically
- clean_expansion_vert (mask, w, 0, 0, w, h, 1);
- clean_expansion_vert (mask, w, w, 0, 0, h, 1);
- clean_expansion_horz (mask, w, 0, 0, w, h, 1);
- clean_expansion_horz (mask, w, 0, h, w, 0, 1);
-
- do // coarse expand the diff pixels
- { // merge would-be diff rectangles in the process
- cnt = 0;
- for (y = 0; y < h; y++)
- {
- for (x = 0; x < w; x++)
- {
- if (mask[y * w + x] != 0)
- cnt += expand_rect (mask, x, y, w, h);
- }
- }
- // repeat is something was expanded
- } while (cnt != 0);
- // at this point we should have guaranteed non-overlapping
- // rectangles that cover all of the delta areas
- if (opts.sectorsize)
- { // final expansion cleanup
- for (y = 0; y < h; y += opts.sectorsize)
- {
- for (x = 0; x < w; x += opts.sectorsize)
- {
- int x2, y2;
- cnt = 0;
- for (y2 = y; y2 < y + opts.sectorsize && y2 < h; y2++)
- for (x2 = x; x2 < x + opts.sectorsize && x2 < w; x2++)
- if (mask[y2 * w + x2] == 1)
- cnt++;
- if (cnt > 0)
- continue; // dirty sector
- // clean up sector
- for (y2 = y; y2 < y + opts.sectorsize && y2 < h; y2++)
- for (x2 = x; x2 < x + opts.sectorsize && x2 < w; x2++)
- mask[y2 * w + x2] = 0;
- }
- }
- }
- // check how muany pixels have to be replaced
- for (x = 0, cnt = 0; x < w * h; x++)
- if (mask[x])
- cnt++;
- if (opts.deltamask)
- create_mask_png (mask, w, h);
- // generate delta images
- if (cnt != w * h)
- {
- int ofs;
- for (y = 0, ofs = 0; y < h; y++)
- {
- for (x = 0; x < w; x++, ofs++)
- {
- if (mask[ofs] != 0)
- { // copy masked rectangle into a new image
- // and clear the mask
- int i;
- int rw, rh;
- int x2, y2;
- unsigned char* src;
- unsigned char* dst;
- file_info* ni;
-
- ni = file_info_add_image (i1);
- if (!ni)
- {
- x = w;
- y = h;
- break;
- }
-
- // lookup delta rectangle
- for (i = x, src = mask + ofs; i < w && *src != 0; i++, src++)
- ;
- ni->w = rw = i - x;
- if (opts.fullrects)
- { // locate only complete rectangles
- y2 = y + 1;
- for (i = y + 1, src = mask + ofs; i < h && *src != 0; i++, src += w)
- {
- unsigned char* src2;
- y2 = i;
- for (x2 = x, src2 = src; x2 < x + rw && *src2 != 0; x2++, src2++)
- ;
- if (x2 < x + rw)
- break;
- }
- }
- else
- { // any rectangles
- for (y2 = y + 1, src = mask + ofs; y2 < h && *src != 0; y2++, src += w)
- ;
- }
-
- ni->h = rh = y2 - y;
- ni->indimg = (unsigned char*) malloc (rw * rh);
- if (!ni->indimg)
- {
- x = w;
- y = h;
- break;
- }
- // copy the pixels
- for (i = 0, src = i1->indimg + (dy1 + y) * i1->w + dx1 + x, dst = ni->indimg;
- i < rh;
- i++, src += i1->w, dst += rw
- )
- {
- memcpy (dst, src, rw);
- memset (mask + ofs + i * w, 0, rw);
- }
- ni->x = i1->x + dx1 + x;
- ni->y = i1->y + dy1 + y;
- }
- }
- }
- if (i1->next)
- { // dispose of the original
- file_info* ni = i1->next;
- free (i1->indimg);
- i1->indimg = ni->indimg;
- i1->x = ni->x;
- i1->y = ni->y;
- i1->w = ni->w;
- i1->h = ni->h;
- i1->next = ni->next;
-
- free (ni);
- }
- }
- else
- { // break here
- cnt = 1;
- }
- if (mask)
- free (mask);
- return 0;
- }
- void
- delta_images (void)
- {
- int i;
- unsigned short nextid = 0x101;
- verbose ("calculating frame image deltas", 0);
- Infos[iback].objid = nextid++;
- if (iback != 0)
- { // set the first frame objid different
- // from back id
- Infos[0].objid = nextid++;
- }
- // remove dupes
- for (i = 1; i < cFiles; i++)
- {
- int i2;
-
- if (i == iback)
- continue;
- Infos[i].objid = Infos[i - 1].objid;
- for (i2 = i - 1; i2 >= 0 && Infos[i].identical == -1; i2--)
- {
- int orgi2 = i2;
- // deference identical chain
- while (Infos[i2].identical != -1)
- i2 = Infos[i2].identical;
- if (equal_images (i, i2))
- {
- Infos[i].identical = i2;
- // dont need image data anymore
- if (Infos[i].indimg)
- {
- free (Infos[i].indimg);
- Infos[i].indimg = 0;
- }
- if (orgi2 != i - 1)
- { // detached descendant
- // clone the object for it
- if (Infos[i2].clone == -1)
- { // no clones yet
- Infos[i2].cloneid = nextid++;
- Infos[i2].clone = i;
- Infos[i].objid = Infos[i2].cloneid;
- }
- else
- { // already cloned for another frame
- // tell the frame to preclone it for
- // this frame too
- // dereference preclone chain first
- for (i2 = Infos[i2].clone; Infos[i2].preclone != -1; i2 = Infos[i2].preclone)
- ;
- Infos[i2].preclone = i;
- Infos[i2].precloneid = nextid++;
- Infos[i].objid = Infos[i2].precloneid;
- }
- }
- }
- }
- verbose (".", 0);
- }
- verbose ("|", 0);
- // compute deltas
- for (i = cFiles - 1; i >= 0; i--)
- {
- int i2;
- if (i == iback || Infos[i].identical != -1)
- // no delta needed
- continue;
- else
- {
- if (i == 0 && i != iback)
- { // delta against original background
- i2 = iback;
- }
- else
- { // deref indentical chain
- for (i2 = i - 1; i2 >= 0 && Infos[i2].identical != -1; i2 = Infos[i2].identical)
- ;
-
- // sanity check
- if (Infos[i2].objid != Infos[i].objid)
- {
- fprintf (stderr, "delta_images: logical error 1\n");
- exit(EXIT_FAILURE);
- }
- }
- // debug info
- _curframe = i;
- _curdeltaframe = i2;
- build_delta (Infos + i, Infos + i2);
- }
- verbose (".", 0);
- }
-
- verbose ("\n", 0);
- }
- int
- get_png_image_data (file_info* ms, unsigned char* imgdata)
- {
- int i;
- for (i = 0; i < ms->w * ms->h; i++, imgdata++)
- {
- if (i % ms->w == 0)
- { // write filter byte
- *imgdata++ = 0;
- }
-
- *imgdata = ms->indimg[i];
- }
- return 0;
- }
- int
- compress_png (file_info* ms, mng_ptr imgdata, mng_uint32 imglen, mng_uint32p bytes)
- {
- int ret = 0;
- unsigned char* tempdata;
- uLong srcLen;
- *bytes = 0;
- // extra byte in front of each line for filter type
- srcLen = ms->w * ms->h + ms->h;
- tempdata = (mng_ptr) malloc(srcLen);
- if (!tempdata)
- return 241;
- ret = get_png_image_data (ms, tempdata);
- if (!ret)
- {
- uLong dstLen = imglen;
- if (Z_OK == compress2(imgdata, &dstLen, tempdata, srcLen, 9))
- *bytes = dstLen;
- else
- ret = 253;
- }
-
- free(tempdata);
- return ret;
- }
- int
- output_png (mng_handle mng, file_info* rf, int delta)
- {
- int ret = 0;
- mng_ptr imgdata;
- mng_uint32 imglen;
- mng_uint32 cbcomp;
- unsigned short objid = rf->objid;
- // maximum necessary space
- // deflated data can be 100.1% + 12 bytes in worst case
- imglen = mngw * mngh + mngh;
- imglen += imglen / 100 + 20; // extra 8 for safety
- imgdata = (mng_ptr) malloc(imglen);
- if (!imgdata)
- return 252;
- do
- {
- if (delta)
- { // output delta
- ret = mng_putchunk_dhdr (mng, objid,
- MNG_IMAGETYPE_PNG, MNG_DELTATYPE_BLOCKPIXELREPLACE,
- rf->w, rf->h, rf->x, rf->y);
- }
- else
- { // output image verbatim
- ret = mng_putchunk_ihdr (mng, rf->w, rf->h,
- MNG_BITDEPTH_8, MNG_COLORTYPE_INDEXED, MNG_COMPRESSION_DEFLATE,
- MNG_FILTER_ADAPTIVE, MNG_INTERLACE_NONE);
- if (ret == MNG_NOERROR)
- { // write empty PLTE to use the global PLTE
- ret = mng_putchunk_plte (mng, 0, Pal);
- //ret = mng_putchunk_plte (mng, cPalClr, Pal); // enable to write plain PNG
- }
- }
- if (ret == MNG_NOERROR)
- ret = compress_png (rf, imgdata, imglen, &cbcomp);
-
- if (ret == MNG_NOERROR)
- ret = mng_putchunk_idat (mng, cbcomp, imgdata);
-
- if (ret == MNG_NOERROR)
- ret = mng_putchunk_iend (mng);
- } while ((rf = rf->next) != 0 && ret == MNG_NOERROR);
- free (imgdata);
- return ret;
- }
- int
- write_mng_file (void)
- {
- int ret = 0;
- mng_handle mng;
- file_info wf;
- file_info rf;
- file_info backf;
- int i;
- unsigned short lastobjid;
- char curframemode, newframemode;
- mng = mng_initialize (MNG_NULL, mng_alloc, mng_free, MNG_NULL);
- if (mng == MNG_NULL)
- {
- fprintf (stderr, "libmng did not init properly\n");
- return 250;
- }
- // set the callbacks
- mng_setcb_openstream(mng, mng_open_stream);
- mng_setcb_closestream(mng, mng_close_stream);
- mng_setcb_writedata(mng, mng_write_stream);
- file_info_init (&wf);
- wf.fname = opts.outfile;
- wf.fmode = "wb";
- mng_set_userdata (mng, &wf);
- ret = mng_create (mng);
- if (ret != MNG_NOERROR)
- fprintf (stderr, "Could not create '%s'\n", wf.fname);
- else
- verbose ("writing MNG file '%s'", wf.fname);
- ret = mng_putchunk_mhdr (mng, mngw, mngh, timerate, 0, 0, 0,
- MNG_SIMPLICITY_VALID | MNG_SIMPLICITY_SIMPLEFEATURES |
- MNG_SIMPLICITY_COMPLEXFEATURES | MNG_SIMPLICITY_DELTAPNG | 0x240);
-
- //ret = mng_putchunk_term (mng, MNG_TERMACTION_LASTFRAME, MNG_ITERACTION_LASTFRAME, 0, 0);
- ret = mng_putchunk_plte (mng, cPalClr, Pal);
- ret = mng_putchunk_back (mng, 0,0,0, 0, 0, MNG_BACKGROUNDIMAGE_NOTILE);
- curframemode = MNG_FRAMINGMODE_1;
- ret = mng_putchunk_fram (mng, MNG_FALSE, curframemode, 0,MNG_NULL,
- MNG_CHANGEDELAY_DEFAULT, MNG_CHANGETIMOUT_NO, MNG_CHANGECLIPPING_NO, MNG_CHANGESYNCID_NO,
- framedelay, 0,0,0,0,0,0, MNG_NULL,0);
- // define the staring image/object
- backf = Infos[iback];
- ret = mng_putchunk_defi (mng, backf.objid, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE, MNG_FALSE, 0,0, MNG_FALSE, 0,0,0,0);
- ret = output_png (mng, &backf, 0);
- //ret = mng_putchunk_save (mng, MNG_TRUE, 0,0);
- //ret = mng_putchunk_seek (mng, 5, "start");
- if (iback != 0)
- { // clone the starting object for the first frame
- ret = mng_putchunk_clon (mng, backf.objid, Infos[0].objid,
- MNG_FULL_CLONE, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE_ASPARENT,
- MNG_FALSE, 0,0,0);
- }
- lastobjid = 0;
- for (i = 0; i < cFiles && ret == MNG_NOERROR; i++)
- {
- rf = Infos[i];
- if (rf.precloneid != 0)
- { // pre-clone the object for another frame
- ret = mng_putchunk_clon (mng, rf.objid, rf.precloneid,
- MNG_FULL_CLONE, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE_ASPARENT,
- MNG_FALSE, 0,0,0);
- }
- if (is_multi_delta_image (&rf))
- // multi-delta png; frame mode: 0-delay for subframe
- newframemode = MNG_FRAMINGMODE_2;
- else
- // frame mode: 1 image per frame
- newframemode = MNG_FRAMINGMODE_1;
- if (newframemode != curframemode)
- { // change framing mode only
- ret = mng_putchunk_fram (mng, MNG_FALSE, newframemode, 0,MNG_NULL,
- MNG_CHANGEDELAY_NO, MNG_CHANGETIMOUT_NO, MNG_CHANGECLIPPING_NO, MNG_CHANGESYNCID_NO,
- 0,0,0,0,0,0,0, MNG_NULL,0);
- curframemode = newframemode;
- }
- else if (curframemode == MNG_FRAMINGMODE_2)
- { // start new subframe
- ret = mng_putchunk_fram (mng, MNG_TRUE, 0,0,MNG_NULL,0,0,0,0,0,0,0,0,0,0,0,0,0);
- }
- if (rf.indimg != 0 && i != iback)
- { // display a delta png
- ret = output_png (mng, &rf, 1);
- }
- if (rf.cloneid != 0)
- { // post-clone the object for another frame
- ret = mng_putchunk_clon (mng, rf.objid, rf.cloneid,
- MNG_FULL_CLONE, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE_ASPARENT,
- MNG_FALSE, 0,0,0);
- }
- if (rf.objid != lastobjid || rf.identical != -1)
- { // show the object for this frame
- ret = mng_putchunk_show (mng, MNG_FALSE, rf.objid, rf.objid, MNG_SHOWMODE_0);
- lastobjid = rf.objid;
- }
- verbose (".", 0);
- }
- //ret = mng_putchunk_seek (mng, 3, "end");
- ret = mng_putchunk_mend (mng);
- ret = mng_write (mng);
- file_info_cleanup (&wf);
- mng_cleanup (&mng);
- if (ret == MNG_NOERROR)
- verbose ("finished.\n", 0);
- else
- fprintf (stderr, "Could not create MNG file\n");
- return ret;
- }
- mng_ptr MNG_DECL
- mng_alloc (mng_size_t iLen)
- {
- mng_ptr ptr;
- if (iLen & 0x80000000)
- return 0; // MNG error!
- ptr = malloc (iLen);
- if (ptr)
- memset(ptr, 0, iLen);
-
- return ptr;
- }
- void MNG_DECL
- mng_free (mng_ptr pPtr, mng_size_t iLen)
- {
- if (iLen)
- free (pPtr);
- }
- mng_bool MNG_DECL
- mng_open_stream (mng_handle mng)
- {
- file_info* ms;
- ms = (file_info*) mng_get_userdata (mng);
-
- ms->f = fopen (ms->fname, ms->fmode);
- if (!ms->f)
- {
- fprintf(stderr, "unable to open '%s'\n", ms->fname);
- return MNG_FALSE;
- }
- return MNG_TRUE;
- }
- mng_bool MNG_DECL
- mng_close_stream (mng_handle mng)
- {
- file_info* ms;
- ms = (file_info*) mng_get_userdata (mng);
- fclose(ms->f);
- ms->f = NULL;
- return MNG_TRUE;
- }
- mng_bool MNG_DECL
- mng_read_stream (mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes)
- {
- file_info* ms;
- ms = (file_info*) mng_get_userdata (mng);
- *bytes = fread(buffer, 1, size, ms->f);
- return MNG_TRUE;
- }
- mng_bool MNG_DECL
- mng_write_stream (mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes)
- {
- file_info* ms;
- ms = (file_info*) mng_get_userdata (mng);
- *bytes = fwrite(buffer, 1, size, ms->f);
- return MNG_TRUE;
- }
- mng_bool MNG_DECL
- mng_process_header (mng_handle mng, mng_uint32 width, mng_uint32 height)
- {
- file_info* ms;
- ms = (file_info*) mng_get_userdata (mng);
- ms->w = width;
- ms->h = height;
- ms->image = (RGBA*) malloc(sizeof(RGBA) * width * height);
- if (!ms->image)
- return MNG_FALSE;
- mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8);
- return MNG_TRUE;
- }
- mng_ptr MNG_DECL
- mng_get_canvasline_read (mng_handle mng, mng_uint32 line)
- {
- file_info* ms;
- mng_ptr row;
- ms = (file_info*) mng_get_userdata (mng);
- row = ms->image + ms->w * line;
-
- return row;
- }
- mng_ptr MNG_DECL
- mng_get_canvasline_write (mng_handle mng, mng_uint32 line)
- {
- file_info* ms;
- ms = (file_info*) mng_get_userdata (mng);
- //if (!ms->rowdata)
- // ms->rowdata = (unsigned char*) malloc (ms->w);
- //if (!ms->rowdata)
- // return MNG_NULL;
- //make_pal_row (ms, line, ms->rowdata);
- // satisfying compiler
- line = 0;
- return MNG_NULL;
- }
- mng_bool MNG_DECL
- mng_refresh_display (mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h)
- {
- // not implemented
- file_info* ms;
- ms = (file_info*) mng_get_userdata (mng);
- // satisfying compiler
- x = y = w = h = 0;
- return MNG_TRUE;
- }
- mng_uint32 MNG_DECL
- mng_get_tickcount (mng_handle mng)
- {
- // not implemented
- file_info* ms;
- static int tick = 0;
- ms = (file_info*) mng_get_userdata (mng);
- return tick += 50;
- }
- mng_bool MNG_DECL
- mng_set_timer (mng_handle mng, mng_uint32 msecs)
- {
- // not implemented
- file_info* ms;
- ms = (file_info*) mng_get_userdata (mng);
- ms->delay = msecs;
- return MNG_TRUE;
- }