diff --git a/tools/mng/Makefile b/tools/mng/Makefile new file mode 100755 index 000000000..3729cfd1b --- /dev/null +++ b/tools/mng/Makefile @@ -0,0 +1,37 @@ +######################################################################## +# This is a GNU makefile - tested on CYGWIN only though. +# +# You may need to compile or install libmng before compiling this. +# Libmng also requires zlib, jpeg and lcms +# + +TARGET = makemng +# These flags are needed to get it to compile with libmng.dll on CYGWIN +# CYGWINFLAGS = -DMNG_USE_DLL +CYGWINFLAGS = +MNGFLAGS = -I. -DMNG_SKIP_LCMS -DMNG_SKIP_IJG6B \ + -DMNG_SUPPORT_READ -DMNG_SUPPORT_WRITE -DMNG_SUPPORT_DISPLAY \ + -DMNG_ACCESS_CHUNKS + +CC = gcc +CFLAGS = -W -Wall -O0 # -g +LIBS = -L/usr/local/lib -lmng -lm -lz -ljpeg -llcms +LDFLAGS= + +BINS = $(TARGET) $(TARGET).exe +SRCS = makemng.c filelist.c +OBJS = makemng.o filelist.o + +.SUFFIXES: .c .o + +.c.o: + $(CC) -c $(CFLAGS) -o $@ $*.c $(MNGFLAGS) $(CYGWINFLAGS) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -o $(TARGET) $(OBJS) $(LIBS) $(LDFLAGS) + +clean: + rm -f $(BINS) *.o + diff --git a/tools/mng/Release/makemng.exe b/tools/mng/Release/makemng.exe new file mode 100755 index 000000000..8348ce60b Binary files /dev/null and b/tools/mng/Release/makemng.exe differ diff --git a/tools/mng/filelist.c b/tools/mng/filelist.c new file mode 100755 index 000000000..93a54b9c4 --- /dev/null +++ b/tools/mng/filelist.c @@ -0,0 +1,276 @@ +/* + * 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. + */ + +/* This file contains two versions of LoadDirEntryTable; one for Windows, and + * one for other systems (POSIX). + */ +#ifdef WIN32 +#include +#include +#include +#include + +char** +make_file_list (const char* pattern, int* pnum_entries) +{ + int num_entries, length; + char** StringTable; + char** lpLastOffs; + + num_entries = 0; + StringTable = 0; + do + { + int slen; + long handle; + struct _finddata_t f; + + if (num_entries == 0) + length = 0; + else + { + slen = (num_entries + 1) * sizeof (char*); + StringTable = (char**) malloc (slen + length); + if (StringTable == 0) + break; + + lpLastOffs = StringTable; + *lpLastOffs = (char*)StringTable + slen; + + num_entries = 0; + length = slen; + } + + handle = _findfirst (pattern, &f); + if (handle != -1) + { + do + { + if (f.attrib & (_A_HIDDEN | _A_SUBDIR | _A_SYSTEM)) + continue; + + slen = strlen (f.name) + 1; + length += slen; + + if (StringTable) + { + char *lpStr; + char **lpLo, **lpHi; + + lpLo = StringTable; + lpHi = lpLastOffs - 1; + while (lpLo <= lpHi) + { + char c1, c2; + char *pStr; + int LocLen; + char **lpMid; + + lpMid = lpLo + ((lpHi - lpLo) >> 1); + + LocLen = lpMid[1] - lpMid[0]; + if (LocLen > slen) + LocLen = slen; + + lpStr = lpMid[0]; + pStr = f.name; + while (LocLen-- + && (c1 = toupper (*lpStr++)) + == (c2 = toupper (*pStr++))) + ; + + if (c1 <= c2) + lpLo = lpMid + 1; + else + lpHi = lpMid - 1; + } + + lpStr = lpLo[0]; + memmove (lpStr + slen, lpStr, lpLastOffs[0] - lpLo[0]); + strcpy (lpStr, f.name); + + for (lpHi = lpLastOffs++; lpHi >= lpLo; --lpHi) + lpHi[1] = lpHi[0] + slen; + } + + ++num_entries; + + } while (_findnext (handle, &f) == 0); + + _findclose (handle); + } + } while (num_entries && StringTable == 0); + + if (StringTable == 0) + *pnum_entries = 0; + else + *pnum_entries = num_entries; + + return StringTable; +} + +#else /* ! defined(WIN32) */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char** +make_file_list (const char* pattern, int* pnum_entries) +{ + size_t num_entries, length; + char** StringTable; + char** lpLastOffs; + char* slash; // Pointer inside pattern to the last / + char path[PATH_MAX]; // buffer for a filename with path + char* file; // Pointer inside path to the filename + size_t pathlen; // length of path, excluding last / and filename + + slash = (char*) strrchr ((const char *) pattern, '/'); + if (slash == NULL) + { + pathlen = 1; + path[0] = '.'; + } + else + { + pathlen = slash - pattern; + memcpy (path, pattern, pathlen); + pattern = slash + 1; + } + file = path + pathlen + 1; + + num_entries = 0; + StringTable = 0; + do + { + int slen; + DIR *handle; + + if (num_entries == 0) + length = 0; + else + { + slen = (num_entries + 1) * sizeof (char*); + StringTable = (char**) malloc (slen + length); + if (StringTable == 0) + break; + + lpLastOffs = StringTable; + *lpLastOffs = (char*)StringTable + slen; + + num_entries = 0; + length = slen; + } + + path[pathlen] = '\0'; // strip any file part + handle = opendir((const char *) path); + if (handle != NULL) + { + path[pathlen] = '/'; + // change the 0 char to a slash; a filename will be + // attached here. + while (1) + { + struct dirent *de; + struct stat sb; + + de = readdir(handle); + if (de == NULL) + break; + if (de->d_name[0] == '.') + continue; + strcpy (file, de->d_name); + // attach the filename to path + if (stat(path, &sb) == -1) + continue; + if (!S_ISREG(sb.st_mode)) + continue; + if (fnmatch(pattern, de->d_name, 0) != 0) + continue; + + slen = strlen (de->d_name) + 1; + length += slen; + + if (StringTable) + { + char *lpStr; + char **lpLo, **lpHi; + + lpLo = StringTable; + lpHi = lpLastOffs - 1; + while (lpLo <= lpHi) + { + char c1, c2; + char *pStr; + int LocLen; + char **lpMid; + + lpMid = lpLo + ((lpHi - lpLo) >> 1); + + LocLen = lpMid[1] - lpMid[0]; + if (LocLen > slen) + LocLen = slen; + + lpStr = lpMid[0]; + pStr = de->d_name; + while (LocLen-- + && (c1 = toupper (*lpStr++)) + == (c2 = toupper (*pStr++))) + ; + + if (c1 <= c2) + lpLo = lpMid + 1; + else + lpHi = lpMid - 1; + } + + lpStr = lpLo[0]; + memmove (lpStr + slen, lpStr, lpLastOffs[0] - lpLo[0]); + strcpy (lpStr, de->d_name); + + for (lpHi = lpLastOffs++; lpHi >= lpLo; --lpHi) + lpHi[1] = lpHi[0] + slen; + } + + ++num_entries; + } + closedir(handle); + } + } while (num_entries && StringTable == 0); + + if (StringTable == 0) + *pnum_entries = 0; + else + *pnum_entries = num_entries; + + return StringTable; +} + +#endif + +void +free_file_list (char** list) +{ + if (list) + free(list); +} diff --git a/tools/mng/makemng.c b/tools/mng/makemng.c new file mode 100755 index 000000000..878b07134 --- /dev/null +++ b/tools/mng/makemng.c @@ -0,0 +1,1765 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +# include +#else +# include +#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] \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; +} diff --git a/tools/mng/makemng.dsp b/tools/mng/makemng.dsp new file mode 100755 index 000000000..c5b5aca22 --- /dev/null +++ b/tools/mng/makemng.dsp @@ -0,0 +1,114 @@ +# Microsoft Developer Studio Project File - Name="makemng" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=makemng - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "makemng.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makemng.mak" CFG="makemng - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "makemng - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "makemng - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=xicl6.exe +RSC=rc.exe + +!IF "$(CFG)" == "makemng - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "." /I "..\..\src" /I "..\..\src\msvc++" /I "..\..\src\getopt" /D "NDEBUG" /D "WINDOWS" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "MNG_USE_DLL" /D "MNG_SKIP_LCMS" /D "MNG_SKIP_IJG6B" /D "ZLIB_DLL" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib uuid.lib zlib.lib libmng.lib /nologo /subsystem:console /machine:I386 /libpath:"." + +!ELSEIF "$(CFG)" == "makemng - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "." /I "..\..\src" /I "..\..\src\msvc++" /I "..\..\src\getopt" /D "_DEBUG" /D "ZLIB_DLL" /D "WINDOWS" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "MNG_USE_DLL" /D "MNG_SKIP_LCMS" /D "MNG_SKIP_IJG6B" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib uuid.lib zlib.lib libmng.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"." + +!ENDIF + +# Begin Target + +# Name "makemng - Win32 Release" +# Name "makemng - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\filelist.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\getopt\getopt.c +# End Source File +# Begin Source File + +SOURCE=.\makemng.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\getopt.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/tools/mng/makemng.dsw b/tools/mng/makemng.dsw new file mode 100755 index 000000000..ab12fac01 --- /dev/null +++ b/tools/mng/makemng.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "makemng"=.\makemng.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### +