1 /* -*- mode: c; tab-width: 4; -*- ---------------------------[for (x)emacs]--
2 
3    $Id: gif2swf.c,v 1.7 2008/02/08 11:43:12 kramm Exp $
4    GIF to SWF converter tool
5 
6    Part of the swftools package.
7 
8    Copyright (c) 2005 Daichi Shinozaki <dseg@shield.jp>
9 
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
23 
24    This file is derived from png2swf.c */
25 
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <gif_lib.h>
29 #include "../lib/rfxswf.h"
30 #include "../lib/args.h"
31 
32 #define MAX_INPUT_FILES 1024
33 #define VERBOSE(x) (global.verbose>=x)
34 #define AS_FIRSTFRAME "if(!n) n=0;"
35 #define AS_LASTFRAME "if(n<%d){n=n+1;gotoAndPlay(1);}else stop();"
36 
37 static struct {
38     float framerate;
39     int max_image_width;
40     int max_image_height;
41     int force_width;
42     int force_height;
43     int nfiles;
44     int verbose;
45     int do_cgi;
46     int version;
47     char *outfile;
48     int imagecount;
49     int loopcount;
50 } global;
51 
52 struct {
53     char *filename;
54 } image[MAX_INPUT_FILES];
55 
56 struct gif_header {
57     int width;
58     int height;
59 };
60 
61 enum disposal_method {
62     NONE,
63     DO_NOT_DISPOSE,
64     RESTORE_TO_BGCOLOR,
65     RESTORE_TO_PREVIOUS
66 };
67 
68 static void
69 #if GIFLIB_MAJOR >= 5
localPrintGifError(int ErrorCode)70 localPrintGifError(int ErrorCode)
71 #else
72 localPrintGifError(void)
73 #endif
74 {
75 #if GIFLIB_MAJOR >= 5
76     char *Err = GifErrorString(ErrorCode);
77 #else
78     char *Err = GifErrorString();
79     int ErrorCode = GifError();
80 #endif
81 
82     if (Err != NULL)
83         fprintf(stderr, "\nGIF-LIB error: %s.\n", Err);
84     else
85         fprintf(stderr, "\nGIF-LIB undefined error %d.\n", ErrorCode);
86 }
87 
SetFrameAction(TAG ** t,const char * src,int ver)88 void SetFrameAction(TAG ** t, const char *src, int ver)
89 {
90     ActionTAG *as;
91 
92     as = swf_ActionCompile(src, ver);
93     if (!as)
94         fprintf(stderr, "Couldn't compile ActionScript\n");
95     else {
96         *t = swf_InsertTag(*t, ST_DOACTION);
97         swf_ActionSet(*t, as);
98         swf_ActionFree(as);
99     }
100 }
101 
getGifDisposalMethod(GifFileType * gft,int framenum)102 int getGifDisposalMethod(GifFileType * gft, int framenum)
103 {
104     int i;
105     ExtensionBlock *ext = gft->SavedImages[framenum].ExtensionBlocks;
106 
107     for (i = 0; i < gft->SavedImages[framenum].ExtensionBlockCount; i++, ext++)
108         if (ext->Function == GRAPHICS_EXT_FUNC_CODE)
109             return ((ext->Bytes[0] & 0x1C) >> 2);
110 
111     return -1;
112 }
113 
getGifLoopCount(GifFileType * gft)114 int getGifLoopCount(GifFileType * gft)
115 {
116     int i, loop = -1;
117     ExtensionBlock *ext = gft->SavedImages[0].ExtensionBlocks;
118 
119     for (i = 0; i < gft->SavedImages[0].ExtensionBlockCount; i++, ext++)
120         if (ext->Function == APPLICATION_EXT_FUNC_CODE) {
121             // info: http://semmix.pl/color/exgraf/eeg24.htm
122             if (ext->ByteCount == 11 &&
123                 (strncmp(&ext->Bytes[0], "NETSCAPE2.0", 11) == 0 ||
124                  strncmp(&ext->Bytes[0], "ANIMEXTS1.0", 11) == 0)) {
125                 // check for the subblock
126                 ext++;
127                 if (ext->ByteCount != 3)
128                     ext--;
129                 else {
130                     loop = GET16(&ext->Bytes[1]);
131                     break;
132                 }
133             }
134         }
135 
136     return loop;
137 }
138 
getGifDelayTime(GifFileType * gft,int framenum)139 U16 getGifDelayTime(GifFileType * gft, int framenum)
140 {
141     int i;
142     ExtensionBlock *ext = gft->SavedImages[framenum].ExtensionBlocks;
143 
144     for (i = 0; i < gft->SavedImages[framenum].ExtensionBlockCount; i++, ext++)
145         if (ext->Function == GRAPHICS_EXT_FUNC_CODE)
146             return GET16(&ext->Bytes[1]);
147 
148     return 0;
149 }
150 
getTransparentColor(GifFileType * gft,int framenum)151 int getTransparentColor(GifFileType * gft, int framenum)
152 {
153     int i;
154     ExtensionBlock *ext = gft->SavedImages[framenum].ExtensionBlocks;
155 
156     // Get transparency color from graphic extension block
157     for (i = 0; i < gft->SavedImages[framenum].ExtensionBlockCount; i++, ext++)
158         if ((ext->Function == GRAPHICS_EXT_FUNC_CODE) && (ext->Bytes[0] & 1)) {
159             // there is a transparent color
160             return ext->Bytes[3] == 0 ? 0 :     // exception
161                 (U8) ext->Bytes[3];             // transparency color
162         }
163 
164     return -1;
165 }
166 
MovieStart(SWF * swf,float framerate,int dx,int dy)167 TAG *MovieStart(SWF * swf, float framerate, int dx, int dy)
168 {
169     TAG *t;
170     RGBA rgb;
171 
172     memset(swf, 0x00, sizeof(SWF));
173 
174     swf->fileVersion = global.version;
175     swf->frameRate = (int) (256.0 * framerate);
176     swf->movieSize.xmax = dx * 20;
177     swf->movieSize.ymax = dy * 20;
178 
179     t = swf->firstTag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
180 
181     rgb.r = rgb.g = rgb.b = rgb.a = 0x00;
182 
183     //rgb.g = 0xff; //<--- handy for testing alpha conversion
184     swf_SetRGB(t, &rgb);
185 
186     return t;
187 }
188 
MovieFinish(SWF * swf,TAG * t,char * sname)189 int MovieFinish(SWF * swf, TAG * t, char *sname)
190 {
191     int f, so = fileno(stdout);
192     t = swf_InsertTag(t, ST_END);
193 
194     if ((!isatty(so)) && (!sname))
195         f = so;
196     else {
197         if (!sname)
198             sname = "output.swf";
199         f = open(sname, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
200     }
201 
202     if (global.do_cgi) {
203         if FAILED
204             (swf_WriteCGI(swf)) fprintf(stderr, "WriteCGI() failed.\n");
205     } else {
206         if (swf_WriteSWF(f, swf) < 0)
207             fprintf(stderr, "Unable to write output file: %s\n", sname);
208         if (f != so)
209             close(f);
210     }
211 
212     swf_FreeTags(swf);
213     return 0;
214 }
215 
MovieAddFrame(SWF * swf,TAG * t,char * sname,int id,int imgidx)216 TAG *MovieAddFrame(SWF * swf, TAG * t, char *sname, int id, int imgidx)
217 {
218     SHAPE *s;
219     SRECT r;
220     MATRIX m;
221     int fs;
222 
223     U8 *imagedata, *from, *to;
224     GifImageDesc *img;
225     RGBA *pal;
226 
227     struct gif_header header;
228 
229     int i, j, numcolors, alphapalette;
230     U8 bgcolor;
231     int bpp;                    // byte per pixel
232     int swf_width, padlen;
233 
234     ColorMapObject *colormap;
235     GifColorType c;
236     int interlacedOffset[] = { 0, 4, 2, 1 };    // The way Interlaced image should
237     int interlacedJumps[] = { 8, 8, 4, 2 };     // be read - offsets and jumps...
238     U16 delay, depth;
239     int disposal;
240     char *as_lastframe;
241 
242     GifFileType *gft;
243     FILE *fi;
244 
245     if ((fi = fopen(sname, "rb")) == NULL) {
246         if (VERBOSE(1))
247             fprintf(stderr, "Read access failed: %s\n", sname);
248         return t;
249     }
250     fclose(fi);
251 
252 #if GIFLIB_MAJOR >= 5
253     if ((gft = DGifOpenFileName(sname, NULL)) == NULL) {
254 #else
255     if ((gft = DGifOpenFileName(sname)) == NULL) {
256 #endif
257         fprintf(stderr, "%s is not a GIF file!\n", sname);
258         return t;
259     }
260 
261     if (DGifSlurp(gft) != GIF_OK) {
262 #if GIFLIB_MAJOR >= 5
263         localPrintGifError(gft->Error);
264 #else
265         localPrintGifError();
266 #endif
267         return t;
268     }
269 
270     header.width = gft->SWidth;
271     header.height = gft->SHeight;
272 
273     pal = (RGBA *) malloc(256 * sizeof(RGBA));
274     memset(pal, 0, 256 * sizeof(RGBA));
275 
276     img = &gft->SavedImages[imgidx].ImageDesc;
277 
278     // Local colormap has precedence over Global colormap
279     colormap = img->ColorMap ? img->ColorMap : gft->SColorMap;
280     numcolors = colormap->ColorCount;
281     alphapalette = getTransparentColor(gft, imgidx);
282     if (VERBOSE(3))
283         fprintf(stderr, "transparent palette index => %d\n", alphapalette);
284     bpp = (alphapalette >= 0 ? 4 : 3);
285 
286     // bgcolor is the background color to fill the bitmap
287     if (gft->SColorMap)         // There is a GlobalColorMap
288         bgcolor = (U8) gft->SBackGroundColor;   // The SBGColor is meaningful
289     else if (alphapalette >= 0) // There is a transparency color
290         bgcolor = alphapalette; // set the bgcolor to tranparent
291     else
292         bgcolor = 0;
293     // Don't know what to do here.
294     // If this doesn't work, we could
295     // create a new color and set the
296     // alpha-channel to transparent
297     // (unless we are using all the 256
298     // colors, in which case either we
299     // give up, or move to 16-bits palette
300     if (VERBOSE(3))
301         fprintf(stderr, "BG palette index => %u\n", bgcolor);
302 
303     for (i = 0; i < numcolors; i++) {
304         c = colormap->Colors[i];
305         if (i == bgcolor || i == alphapalette)
306             pal[i].r = pal[i].g = pal[i].b = pal[i].a = 0;      // Fully transparent
307         else {
308             pal[i].r = c.Red;
309             pal[i].g = c.Green;
310             pal[i].b = c.Blue;
311             pal[i].a = 255;     // Fully opaque
312         }
313     }
314 
315     t = swf_InsertTag(t, bpp == 4 ? ST_DEFINEBITSLOSSLESS2 : ST_DEFINEBITSLOSSLESS);
316     swf_SetU16(t, id);          // id
317 
318     // Ah! The Flash specs says scanlines must be DWORD ALIGNED!
319     // (but image width is the correct number of pixels)
320     swf_width = BYTES_PER_SCANLINE(header.width);
321 
322     if ((imagedata = (U8 *) malloc(swf_width * header.height)) == NULL) {
323         fprintf(stderr, "Failed to allocate memory required, aborted.");
324         exit(2);
325     }
326 
327     to = imagedata;
328     from = (U8 *) gft->SavedImages[imgidx].RasterBits;
329 
330     if (swf_width == header.width) {
331         // we are all nicely aligned and don't need to move the bitmap around.
332         // Just copy the bits into the image buffer.
333         if (!gft->Image.Interlace)
334             if (header.width == img->Width && header.height == img->Height)
335                 memcpy(to, from, header.width * header.height);
336             else {              //small screen
337                 for (i = 0; i < header.height; i++, to += header.width) {
338                     memset(to, bgcolor, header.width);
339                     if (i >= img->Top && i < img->Top + img->Height) {
340                         memcpy(to + img->Left, from, img->Width);
341                         from += img->Width;
342                     }
343                 }
344             }
345 
346         else                    // Need to perform 4 passes on the interlaced images
347             for (i = 0; i < 4; i++)
348                 for (j = interlacedOffset[i]; j < header.height;
349                      j += interlacedJumps[i], from += header.width)
350                     memcpy(to + header.width * j, from, header.width);
351     } else {
352         padlen = swf_width - header.width;
353 
354         // here we need to pad the scanline
355         if (!gft->Image.Interlace) {
356             if (header.width == img->Width && header.height == img->Height) {
357                 for (i = 0; i < header.height; i++, from += header.width, to += swf_width) {
358                     memcpy(to, from, header.width);
359                     memset(to + header.width, bgcolor, padlen);
360                 }
361             } else {            //small screen
362                 for (i = 0; i < header.height; i++, to += swf_width) {
363                     memset(to, bgcolor, swf_width);
364                     if (i >= img->Top && i < img->Top + img->Height) {
365                         memcpy(to + img->Left, from, img->Width);
366                         from += img->Width;
367                     }
368                 }
369             }
370         } else {                // Need to perform 4 passes on the interlaced images
371             for (i = 0; i < 4; i++)
372                 for (j = interlacedOffset[i]; j < header.height;
373                      j += interlacedJumps[i], from += header.width) {
374                     memcpy(to + swf_width * j, from, header.width);
375                     memset(to + swf_width * j, bgcolor, padlen);
376                 }
377         }
378     }
379     swf_SetLosslessBitsIndexed(t, header.width, header.height, imagedata, pal, 256);
380 
381     t = swf_InsertTag(t, ST_DEFINESHAPE);
382 
383     swf_ShapeNew(&s);
384     swf_GetMatrix(NULL, &m);
385     m.sx = 20 * 0x10000;
386     m.sy = 20 * 0x10000;
387     fs = swf_ShapeAddBitmapFillStyle(s, &m, id, 0);
388 
389     swf_SetU16(t, id + 1);      // id
390 
391     r.xmin = r.ymin = 0;
392     r.xmax = header.width * 20;
393     r.ymax = header.height * 20;
394     swf_SetRect(t, &r);
395 
396     swf_SetShapeHeader(t, s);
397 
398     swf_ShapeSetAll(t, s, 0, 0, 0, fs, 0);
399     swf_ShapeSetLine(t, s, r.xmax, 0);
400     swf_ShapeSetLine(t, s, 0, r.ymax);
401     swf_ShapeSetLine(t, s, -r.xmax, 0);
402     swf_ShapeSetLine(t, s, 0, -r.ymax);
403 
404     swf_ShapeSetEnd(t);
405 
406     depth = imgidx + 1;
407     if ((imgidx > 0) &&         // REMOVEOBJECT2 not needed at frame 1(imgidx==0)
408         (global.imagecount > 1)) {
409         // check last frame's disposal method
410         if ((disposal = getGifDisposalMethod(gft, imgidx - 1)) >= 0) {
411             switch (disposal) {
412             case NONE:
413                 // [Replace one full-size, non-transparent frame with another]
414                 t = swf_InsertTag(t, ST_REMOVEOBJECT2);
415                 swf_SetU16(t, depth - 1);
416                 if (VERBOSE(3))
417                     fprintf(stdout, "  [none]\n");
418                 break;
419             case DO_NOT_DISPOSE:
420                 // [Any pixels not covered up by the next frame continue to display]
421                 if (VERBOSE(3))
422                     fprintf(stdout, "  [don't dispose]\n");
423                 break;
424             case RESTORE_TO_BGCOLOR:
425                 // [The background color or background tile -rather than a previous frame-
426                 //  shows through transparent pixels]
427                 t = swf_InsertTag(t, ST_REMOVEOBJECT2);
428                 swf_SetU16(t, depth - 2);
429                 if (VERBOSE(3))
430                     fprintf(stdout, "  [restore to bg color]\n");
431                 break;
432             case RESTORE_TO_PREVIOUS:
433                 // [Restores to the state of a previous, undisposed frame]
434                 // ** NOT IMPLEMENTED YET (same as "restore to bgcolor") **
435                 t = swf_InsertTag(t, ST_REMOVEOBJECT2);
436                 swf_SetU16(t, depth - 1);
437                 if (VERBOSE(3))
438                     fprintf(stdout, "  [restore to previous]\n");
439                 break;
440             default:
441                 break;
442             }
443         }
444     }
445 
446     swf_SetU16(t, depth);
447     t = swf_InsertTag(t, ST_PLACEOBJECT2);
448 
449     swf_GetMatrix(NULL, &m);
450     m.tx = (swf->movieSize.xmax - (int) header.width * 20) / 2;
451     m.ty = (swf->movieSize.ymax - (int) header.height * 20) / 2;
452     swf_ObjectPlace(t, id + 1, depth, &m, NULL, NULL);
453 
454     if ((global.imagecount > 1) && (global.loopcount > 0)) { // 0 means infinite loop
455         if (imgidx == 0)
456             SetFrameAction(&t, AS_FIRSTFRAME, global.version);
457     }
458 
459     t = swf_InsertTag(t, ST_SHOWFRAME);
460 
461     if (global.imagecount > 1) { // multi-frame GIF?
462         int framecnt;
463         delay = getGifDelayTime(gft, imgidx); // delay in 1/100 sec
464         framecnt = (int) (global.framerate * (delay / 100.0));
465         if (framecnt > 1) {
466             if (VERBOSE(2))
467                 fprintf(stderr, "at frame %d: pad %d frames(%.3f sec)\n",
468                         imgidx + 1, framecnt, delay / 100.0);
469 
470             framecnt -= 1; // already inserted a frame
471             while (framecnt--)
472                 t = swf_InsertTag(t, ST_SHOWFRAME);
473         }
474         if ((imgidx == global.imagecount - 1) &&global.loopcount > 0) { // last frame
475             as_lastframe = malloc(strlen(AS_LASTFRAME) + 5); // 0-99999
476             sprintf(as_lastframe, AS_LASTFRAME, global.loopcount);
477             SetFrameAction(&t, as_lastframe, global.version);
478             if (as_lastframe)
479                 free(as_lastframe);
480         }
481     }
482 
483     free(pal);
484     free(imagedata);
485 #if GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1 || GIFLIB_MAJOR > 5
486     DGifCloseFile(gft, NULL);
487 #else
488     DGifCloseFile(gft);
489 #endif
490 
491     return t;
492 }
493 
494 int CheckInputFile(char *fname, char **realname)
495 {
496     FILE *fi;
497     char *s = malloc(strlen(fname) + 5);
498     GifFileType *gft;
499 
500     if (!s)
501         exit(2);
502     (*realname) = s;
503     strcpy(s, fname);
504 
505     // Check whether file exists (with typical extensions)
506 
507     if ((fi = fopen(s, "rb")) == NULL) {
508         sprintf(s, "%s.gif", fname);
509         if ((fi = fopen(s, "rb")) == NULL) {
510             sprintf(s, "%s.GIF", fname);
511             if ((fi = fopen(s, "rb")) == NULL) {
512                 sprintf(s, "%s.Gif", fname);
513                 if ((fi = fopen(s, "rb")) == NULL) {
514                     fprintf(stderr, "Couldn't open %s!\n", fname);
515                     return -1;
516                 }
517             }
518         }
519     }
520     fclose(fi);
521 
522 #if GIFLIB_MAJOR >= 5
523     if ((gft = DGifOpenFileName(s, NULL)) == NULL) {
524 #else
525     if ((gft = DGifOpenFileName(s)) == NULL) {
526 #endif
527         fprintf(stderr, "%s is not a GIF file!\n", fname);
528         return -1;
529     }
530 
531     if (global.max_image_width < gft->SWidth)
532         global.max_image_width = gft->SWidth;
533     if (global.max_image_height < gft->SHeight)
534         global.max_image_height = gft->SHeight;
535 
536     if (DGifSlurp(gft) != GIF_OK) {
537 #if GIFLIB_MAJOR >= 5
538         localPrintGifError(gft->Error);
539 #else
540         localPrintGifError();
541 #endif
542         return -1;
543     }
544     // After DGifSlurp() call, gft->ImageCount become available
545     if ((global.imagecount = gft->ImageCount) >1) {
546         if (global.loopcount < 0) {
547             global.loopcount = getGifLoopCount(gft);
548             if (VERBOSE(3))
549                 fprintf(stderr, "Loops => %d\n", global.loopcount);
550         }
551     }
552     if (VERBOSE(2)) {
553         U8 i;
554         fprintf(stderr, "%d x %d, %d images total\n", gft->SWidth, gft->SHeight, gft->ImageCount);
555 
556         for (i = 0; i < gft->ImageCount; i++)
557             fprintf(stderr, "frame: %u, delay: %.3f sec\n", i + 1, getGifDelayTime(gft, i) / 100.0);
558     }
559 
560 #if GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1 || GIFLIB_MAJOR > 5
561     DGifCloseFile(gft, NULL);
562 #else
563     DGifCloseFile(gft);
564 #endif
565 
566     return 0;
567 }
568 
569 int args_callback_option(char *arg, char *val)
570 {
571     int res = 0;
572     if (arg[1])
573         res = -1;
574     else
575         switch (arg[0]) {
576         case 'l':
577             if (val)
578                 global.loopcount = atoi(val);
579             res = 1;
580             break;
581 
582         case 'r':
583             if (val)
584                 global.framerate = atof(val);
585             if ((global.framerate < 1.0 / 256) ||(global.framerate >= 256.0)) {
586                 if (VERBOSE(1))
587                     fprintf(stderr,
588                             "Error: You must specify a valid framerate between 1/256 and 255.\n");
589                 exit(1);
590             }
591             res = 1;
592             break;
593 
594         case 'o':
595             if (val)
596                 global.outfile = val;
597             res = 1;
598             break;
599 
600         case 'z':
601             global.version = 6;
602             res = 0;
603             break;
604 
605         case 'C':
606             global.do_cgi = 1;
607             break;
608 
609         case 'v':
610             if (val)
611                 global.verbose = atoi(val);
612             res = 1;
613             break;
614 
615         case 'X':
616             if (val)
617                 global.force_width = atoi(val);
618             res = 1;
619             break;
620 
621         case 'Y':
622             if (val)
623                 global.force_height = atoi(val);
624             res = 1;
625             break;
626 
627         case 'V':
628             printf("gif2swf - part of %s %s\n", PACKAGE, VERSION);
629             exit(0);
630 
631         default:
632             res = -1;
633             break;
634         }
635 
636     if (res < 0) {
637         if (VERBOSE(1))
638             fprintf(stderr, "Unknown option: -%s\n", arg);
639         exit(1);
640         return 0;
641     }
642     return res;
643 }
644 
645 static struct options_t options[] = {
646 {"r", "rate"},
647 {"o", "output"},
648 {"z", "zlib"},
649 {"l", "loop"},
650 {"X", "pixel"},
651 {"Y", "pixel"},
652 {"v", "verbose"},
653 {"C", "cgi"},
654 {"V", "version"},
655 {0,0}
656 };
657 
658 int args_callback_longoption(char *name, char *val)
659 {
660     return args_long2shortoption(options, name, val);
661 }
662 
663 int args_callback_command(char *arg, char *next) // actually used as filename
664 {
665     char *s;
666     if (CheckInputFile(arg, &s) < 0) {
667         if (VERBOSE(1))
668             fprintf(stderr, "Error opening input file: %s\n", arg);
669         free(s);
670 
671     } else {
672         image[global.nfiles].filename = s;
673         global.nfiles++;
674         if (global.nfiles >= MAX_INPUT_FILES) {
675             if (VERBOSE(1))
676                 fprintf(stderr, "Error: Too many input files.\n");
677             exit(1);
678         }
679     }
680 
681     return 0;
682 }
683 
684 void args_callback_usage(char *name)
685 {
686     printf("\n");
687     printf("Usage: %s [-X width] [-Y height] [-o file.swf] [-r rate] file1.gif [file2.gif ...]\n", name);
688     printf("\n");
689     printf("-r , --rate <framerate>        Set movie framerate (frames per second)\n");
690     printf("-o , --output <filename>       Set name for SWF output file.\n");
691     printf("-z , --zlib <zlib>             Enable Flash 6 (MX) Zlib Compression\n");
692     printf("-l , --loop <loop count>           Set loop count. (default: 0 [=infinite loop])\n");
693     printf("-X , --pixel <width>           Force movie width to <width> (default: autodetect)\n");
694     printf("-Y , --pixel <height>          Force movie height to <height> (default: autodetect)\n");
695     printf("-v , --verbose <level>         Set verbose level (0=quiet, 1=default, 2=debug)\n");
696     printf("-C , --cgi                     For use as CGI- prepend http header, write to stdout\n");
697     printf("-V , --version                 Print version information and exit\n");
698     printf("\n");
699 }
700 
701 int main(int argc, char **argv)
702 {
703     SWF swf;
704     TAG *t;
705 
706     memset(&global, 0x00, sizeof(global));
707 
708     global.framerate = 1.0;
709     global.verbose = 1;
710     global.version = 5;
711     global.loopcount = -1;
712 
713     processargs(argc, argv);
714 
715     if (global.nfiles <= 0) {
716         fprintf(stderr, "No gif files found in arguments\n");
717         return 1;
718     }
719 
720     if (VERBOSE(2))
721         fprintf(stderr, "Processing %i file(s)...\n", global.nfiles);
722 
723     if (global.imagecount > 1)       // multi-frame GIF?
724         if (global.framerate == 1.0) // user not specified '-r' option?
725             global.framerate = 10.0;
726 
727     t = MovieStart(&swf, global.framerate,
728                    global.force_width ? global.force_width : global.max_image_width,
729                    global.force_height ? global.force_height : global.max_image_height);
730     {
731         int i, j;
732         for (i = 0; i < global.nfiles; i++) {
733             if (VERBOSE(3))
734                 fprintf(stderr, "[%03i] %s\n", i, image[i].filename);
735             t = MovieAddFrame(&swf, t, image[i].filename, (i * 2) + 1, 0);
736             for (j = 2; j <= global.imagecount; j++)
737                 t = MovieAddFrame(&swf, t, image[i].filename, (j * 2) - 1, j - 1);
738             free(image[i].filename);
739         }
740     }
741 
742     MovieFinish(&swf, t, global.outfile);
743 
744     return 0;
745 }
746