fixed framerate calculation.
[swftools.git] / src / png2swf.c
1 /* png2swf.c
2
3    PNG to SWF converter tool
4
5    Part of the swftools package.
6
7    Copyright (c) 2002,2003 Matthias Kramm <kramm@quiss.org>
8  
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
22  
23 #include <stdio.h>
24 #include <math.h>
25 #include <fcntl.h>
26 #include <zlib.h>
27 #include "../lib/rfxswf.h"
28 #include "../lib/args.h"
29
30 #define MAX_INPUT_FILES 1024
31 #define VERBOSE(x) (global.verbose>=x)
32
33 struct {
34     float framerate;
35     int max_image_width;
36     int max_image_height;
37     int force_width;
38     int force_height;
39     int nfiles;
40     int verbose;
41     char *outfile;
42 } global;
43
44 struct {
45     char *filename;
46 } image[MAX_INPUT_FILES];
47
48 TAG *MovieStart(SWF * swf, float framerate, int dx, int dy)
49 {
50     TAG *t;
51     RGBA rgb;
52
53     memset(swf, 0x00, sizeof(SWF));
54
55     swf->fileVersion = 5;
56     swf->frameRate = (int)(256.0 * framerate);
57     swf->movieSize.xmax = dx * 20;
58     swf->movieSize.ymax = dy * 20;
59
60     t = swf->firstTag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
61
62     rgb.r = rgb.g = rgb.b = rgb.a = 0x00;
63     //rgb.g = 0xff; <--- handy for testing alpha conversion
64     swf_SetRGB(t, &rgb);
65
66     return t;
67 }
68
69 int MovieFinish(SWF * swf, TAG * t, char *sname)
70 {
71     int handle, so = fileno(stdout);
72     t = swf_InsertTag(t, ST_END);
73
74     if ((!isatty(so)) && (!sname))
75         handle = so;
76     else {
77         if (!sname)
78             sname = "output.swf";
79         handle = open(sname, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, 0666);
80     }
81     if FAILED
82         (swf_WriteSWF(handle, swf)) if (VERBOSE(1))
83             fprintf(stderr, "Unable to write output file: %s\n", sname);
84     if (handle != so)
85         close(handle);
86
87     swf_FreeTags(swf);
88     return 0;
89 }
90
91 int png_read_chunk(char (*head)[4], int*destlen, U8**destdata, FILE*fi)
92 {
93     unsigned int len;
94     if(destlen) *destlen=0;
95     if(destdata) *destdata=0;
96     if(!fread(&len, 4, 1, fi))
97         return 0;
98     if(!fread(head, 4, 1, fi))
99         return 0;
100     len = REVERSESWAP32(len);
101     if(destlen) *destlen = len;
102     if(destdata) {
103         if(len)
104             *destdata = malloc(len);
105         else 
106             *destdata = 0;
107         if(!fread(*destdata, len, 1, fi)) {
108             *destdata = 0;
109             if(destlen) *destlen=0;
110             return 0;
111         }
112         fseek(fi, 4, SEEK_CUR);
113
114     } else {
115         fseek(fi, len+4, SEEK_CUR);
116     }
117     return 1;
118 }
119
120 unsigned int png_get_dword(FILE*fi)
121 {
122     unsigned int a;
123     fread(&a,4,1,fi);
124     return REVERSESWAP32(a);
125 }
126
127 struct png_header
128 {
129     int width;
130     int height;
131     int bpp;
132     int mode;
133 };
134
135 int png_read_header(FILE*fi, struct png_header*header)
136 {
137     char id[4];
138     int len;
139     int ok=0;
140     U8 head[8] = {137,80,78,71,13,10,26,10};
141     U8 head2[8];
142     U8*data;
143     fread(head2,8,1,fi);
144     if(strncmp(head,head2,4))
145         return 0;
146    
147     while(png_read_chunk(&id, &len, &data, fi))
148     {
149         if(VERBOSE(2))
150             printf("%c%c%c%c %d\n", id[0],id[1],id[2],id[3],len);
151         if(!strncasecmp(id, "IHDR", 4)) {
152             char a,b,c,f,i;
153             if(len < 8) exit(1);
154             header->width = REVERSESWAP32(*(U32*)&data[0]);
155             header->height = REVERSESWAP32(*(U32*)&data[4]);
156             a = data[8];      // should be 8
157             b = data[9];      // should be 3(indexed) or 2(rgb)
158
159             c = data[10];     // compression mode (0)
160             f = data[11];     // filter mode (0)
161             i = data[12];     // interlace mode (0)
162         
163             if(VERBOSE(2)) printf("image mode:%d\n", b);
164             if(VERBOSE(2)) printf("bpp: %d\n", a);
165             if(VERBOSE(2)) printf("compression: %d\n", c);
166             if(VERBOSE(2)) printf("filter: %d\n", f);
167             if(VERBOSE(2)) printf("interlace: %d\n", i);
168
169             if(b!=0 && b!=2 && b!=3 && b!=6) {
170                 fprintf(stderr, "Image mode %d not supported!\n", b);
171                 if(b == 4) {
172                     fprintf(stderr, "(This is a grayscale image with alpha channel-\n");
173                     fprintf(stderr, " try converting it into an RGB image with alpha channel)\n");
174                 }
175                 exit(1);
176             }
177             if(a!=8 && (b==2 || b==6)) {
178                 fprintf(stderr, "Bpp %d in mode %d not supported!\n", a);
179                 exit(1);
180             }
181             if(c!=0) {
182                 fprintf(stderr, "Compression mode %d not supported!\n", c);
183                 exit(1);
184             }
185             if(f!=0) {
186                 fprintf(stderr, "Filter mode %d not supported!\n", f);
187                 exit(1);
188             }
189             if(i!=0) {
190                 fprintf(stderr, "Interlace mode %d not supported!\n", i);
191                 exit(1);
192             }
193             if(VERBOSE(2))
194                 printf("%dx%d %d %d %d %d %d\n",header->width, header->height, a,b,c,f,i);
195             header->bpp = a;
196             header->mode = b;
197             ok = 1;
198         } 
199         
200         free(data);
201     }
202     return ok;
203 }
204
205 typedef unsigned char byte;
206 #define ABS(a) ((a)>0?(a):(-(a)))
207 byte inline PaethPredictor (byte a,byte b,byte c)
208 {
209         // a = left, b = above, c = upper left
210         int p = a + b - c;        // initial estimate
211         int pa = ABS(p - a);      // distances to a, b, c
212         int pb = ABS(p - b);
213         int pc = ABS(p - c);
214         // return nearest of a,b,c,
215         // breaking ties in order a,b,c.
216         if (pa <= pb && pa <= pc) 
217                 return a;
218         else if (pb <= pc) 
219                 return b;
220         else return c;
221 }
222
223 void applyfilter3(int mode, U8*src, U8*old, U8*dest, int width)
224 {
225     int x;
226     unsigned char lastr=0;
227     unsigned char lastg=0;
228     unsigned char lastb=0;
229     unsigned char upperlastr=0;
230     unsigned char upperlastg=0;
231     unsigned char upperlastb=0;
232
233     if(mode==0) {
234         for(x=0;x<width;x++) {
235             dest[0] = 255;
236             dest[1] = src[0];
237             dest[2] = src[1];
238             dest[3] = src[2];
239             dest+=4;
240             src+=3;
241         }
242     }
243     else if(mode==1) {
244         for(x=0;x<width;x++) {
245             dest[0] = 255;
246             dest[1] = src[0]+lastr;
247             dest[2] = src[1]+lastg;
248             dest[3] = src[2]+lastb;
249             lastr = dest[1];
250             lastg = dest[2];
251             lastb = dest[3];
252             dest+=4;
253             src+=3;
254         }
255     }
256     else if(mode==2) {
257         for(x=0;x<width;x++) {
258             dest[0] = 255;
259             dest[1] = src[0]+old[1];
260             dest[2] = src[1]+old[2];
261             dest[3] = src[2]+old[3];
262             dest+=4;
263             old+=4;
264             src+=3;
265         }
266     }
267     else if(mode==3) {
268         for(x=0;x<width;x++) {
269             dest[0] = 255;
270             dest[1] = src[0]+(old[1]+lastr)/2;
271             dest[2] = src[1]+(old[2]+lastg)/2;
272             dest[3] = src[2]+(old[3]+lastb)/2;
273             lastr = dest[1];
274             lastg = dest[2];
275             lastb = dest[3];
276             dest+=4;
277             old+=4;
278             src+=3;
279         }
280     }
281     else if(mode==4) {
282         for(x=0;x<width;x++) {
283             dest[0] = 255;
284             dest[1] = src[0]+PaethPredictor(lastr,old[1],upperlastr);
285             dest[2] = src[1]+PaethPredictor(lastg,old[2],upperlastg);
286             dest[3] = src[2]+PaethPredictor(lastb,old[3],upperlastb);
287             lastr = dest[1];
288             lastg = dest[2];
289             lastb = dest[3];
290             upperlastr = old[1];
291             upperlastg = old[2];
292             upperlastb = old[3];
293             dest+=4;
294             old+=4;
295             src+=3;
296         }
297     }    
298
299 }
300
301 void applyfilter4(int mode, U8*src, U8*old, U8*dest, int width)
302 {
303     int x;
304     unsigned char lastr=0;
305     unsigned char lastg=0;
306     unsigned char lastb=0;
307     unsigned char lasta=0;
308     unsigned char upperlastr=0;
309     unsigned char upperlastg=0;
310     unsigned char upperlastb=0;
311     unsigned char upperlasta=0;
312
313     if(mode==0) {
314         for(x=0;x<width;x++) {
315             dest[0] = src[3];
316             dest[1] = src[0];
317             dest[2] = src[1];
318             dest[3] = src[2];
319             dest+=4;
320             src+=4;
321         }
322     }
323     else if(mode==1) {
324         for(x=0;x<width;x++) {
325             dest[0] = src[3]+lasta;
326             dest[1] = src[0]+lastr;
327             dest[2] = src[1]+lastg;
328             dest[3] = src[2]+lastb;
329             lasta = dest[0];
330             lastr = dest[1];
331             lastg = dest[2];
332             lastb = dest[3];
333             dest+=4;
334             src+=4;
335         }
336     }
337     else if(mode==2) {
338         for(x=0;x<width;x++) {
339             dest[0] = src[3]+old[0];
340             dest[1] = src[0]+old[1];
341             dest[2] = src[1]+old[2];
342             dest[3] = src[2]+old[3];
343             dest+=4;
344             old+=4;
345             src+=4;
346         }
347     }
348     else if(mode==3) {
349         for(x=0;x<width;x++) {
350             dest[0] = src[3]+(old[0]+lasta)/2;
351             dest[1] = src[0]+(old[1]+lastr)/2;
352             dest[2] = src[1]+(old[2]+lastg)/2;
353             dest[3] = src[2]+(old[3]+lastb)/2;
354             lastr = dest[1];
355             lastg = dest[2];
356             lastb = dest[3];
357             dest+=4;
358             old+=4;
359             src+=4;
360         }
361     }
362     else if(mode==4) {
363         for(x=0;x<width;x++) {
364             dest[0] = src[3]+PaethPredictor(lasta,old[0],upperlasta);
365             dest[1] = src[0]+PaethPredictor(lastr,old[1],upperlastr);
366             dest[2] = src[1]+PaethPredictor(lastg,old[2],upperlastg);
367             dest[3] = src[2]+PaethPredictor(lastb,old[3],upperlastb);
368             lasta = dest[0];
369             lastr = dest[1];
370             lastg = dest[2];
371             lastb = dest[3];
372             upperlasta = old[0];
373             upperlastr = old[1];
374             upperlastg = old[2];
375             upperlastb = old[3];
376             dest+=4;
377             old+=4;
378             src+=4;
379         }
380     }    
381
382 }
383
384 void applyfilter1(int mode, U8*src, U8*old, U8*dest, int width)
385 {
386     int x;
387     unsigned char last=0;
388     unsigned char upperlast=0;
389
390     if(mode==0) {
391         for(x=0;x<width;x++) {
392             *dest = *src;
393             dest++;
394             src++;
395         }
396     }
397     else if(mode==1) {
398         for(x=0;x<width;x++) {
399             *dest = *src+last;
400             last = *dest;
401             dest++;
402             src++;
403         }
404     }
405     else if(mode==2) {
406         for(x=0;x<width;x++) {
407             *dest = *src+*old;
408             dest++;
409             old++;
410             src++;
411         }
412     }
413     else if(mode==3) {
414         for(x=0;x<width;x++) {
415             *dest = *src+(*old+last)/2;
416             dest++;
417             old++;
418             src++;
419         }
420     }
421     else if(mode==4) {
422         for(x=0;x<width;x++) {
423             *dest = *src+PaethPredictor(last,*old,upperlast);
424             last = *dest;
425             upperlast = *old;
426             dest++;
427             old++;
428             src++;
429         }
430     }    
431
432 }
433
434 TAG *MovieAddFrame(SWF * swf, TAG * t, char *sname, int id)
435 {
436     SHAPE *s;
437     SRECT r;
438     MATRIX m;
439     int fs;
440
441     char tagid[4];
442     int len;
443     U8*data;
444     U8*imagedata;
445     U8*zimagedata=0;
446     unsigned long int imagedatalen;
447     unsigned long int zimagedatalen=0;
448     U8*palette = 0;
449     int palettelen = 0;
450     U8*alphapalette = 0;
451     int alphapalettelen = 0;
452     struct png_header header;
453     int bypp;
454
455     FILE *fi;
456     U8 *scanline;
457
458     if ((fi = fopen(sname, "rb")) == NULL) {
459         if (VERBOSE(1))
460             fprintf(stderr, "Read access failed: %s\n", sname);
461         return t;
462     }
463
464     if(!png_read_header(fi, &header))
465         return 0;
466
467     if(header.mode == 3 || header.mode == 0) bypp = 1;
468     else
469     if(header.mode == 2) bypp = 3;
470     else
471     if(header.mode == 6) bypp = 4;
472     else
473         return 0;
474     imagedatalen = bypp * header.width * header.height + 65536;
475     imagedata = malloc(imagedatalen);
476
477     fseek(fi,8,SEEK_SET);
478     while(!feof(fi))
479     {
480         if(!png_read_chunk(&tagid, &len, &data, fi))
481             break;
482         if(!strncmp(tagid, "IEND", 4)) {
483             break;
484         }
485         if(!strncmp(tagid, "PLTE", 4)) {
486             palette = data;
487             palettelen = len/3;
488             data = 0; //don't free data
489             if(VERBOSE(2))
490                 printf("%d colors in palette\n", palettelen);
491         }
492         if(!strncmp(tagid, "tRNS", 4)) {
493             if(header.mode == 3) {
494                 alphapalette = data;
495                 alphapalettelen = len;
496                 data = 0; //don't free data
497                 if(VERBOSE(2))
498                     printf("found %d alpha colors\n", alphapalettelen);
499             }
500         }
501         if(!strncmp(tagid, "IDAT", 4)) {
502             if(!zimagedata) {
503                 zimagedatalen = len;
504                 zimagedata = malloc(len);
505                 memcpy(zimagedata,data,len);
506             } else {
507                 zimagedata = realloc(zimagedata, zimagedatalen+len);
508                 memcpy(&zimagedata[zimagedatalen], data, len);
509                 zimagedatalen += len;
510             }
511         }
512         if(data)
513             free(data);
514     }
515     
516     if(!zimagedata || uncompress(imagedata, &imagedatalen, zimagedata, zimagedatalen) != Z_OK) {
517         fprintf(stderr, "Couldn't uncompress %s!\n", sname);
518         if(zimagedata)
519             free(zimagedata);
520         return 0;
521     }
522     free(zimagedata);
523
524     if(alphapalette)
525         t = swf_InsertTag(t, ST_DEFINEBITSLOSSLESS2);
526     else
527         t = swf_InsertTag(t, ST_DEFINEBITSLOSSLESS);
528
529     swf_SetU16(t, id);          // id
530     if(header.mode == 2 || header.mode == 6) {
531         U8*data2 = malloc(header.width*header.height*4);
532         int i,s=0;
533         int x,y;
534         int pos=0;
535         int opaque=0;
536         int transparent=0;
537         /* in case for mode 2, the following also performs 24->32 bit conversion */
538         for(y=0;y<header.height;y++) {
539             int mode = imagedata[pos++]; //filter mode
540             U8*src;
541             U8*dest;
542             U8*old;
543             dest = &data2[(y*header.width)*4];
544
545             if(header.bpp == 8)
546             {
547                 /* one byte per pixel */
548                 src = &imagedata[pos];
549                 pos+=header.width*(header.mode==6?4:3);
550             } else {
551                 /* not implemented yet */
552                 exit(1);
553             }
554
555             if(!y) {
556                 memset(data2,0,header.width*4);
557                 old = &data2[y*header.width*4];
558             } else {
559                 old = &data2[(y-1)*header.width*4];
560             }
561             if(header.mode==6)
562                 applyfilter4(mode, src, old, dest, header.width);
563             else if(header.mode==2)
564                 applyfilter3(mode, src, old, dest, header.width);
565         }
566
567 #ifdef HAVE_LIBJPEG
568         /* the image is now compressed and stored in data. Now let's take
569            a look at the alpha values to determine which bitmap type we
570            should write */
571         if(header.mode == 6)
572         for(y=0;y<header.height;y++) {
573             U8*l = &data2[(y*header.width)*4];
574             for(x=0;x<header.width;x++) {
575                 if(l[x*4+0]==255) transparent++;
576                 if(l[x*4+0]==0) opaque++;
577             }
578         }
579         /* mode 6 images which are not fully opaque or fully transparent
580            will be stored as definejpeg3 */
581         if(header.mode == 6 && transparent != header.width*header.height
582                             && opaque != header.width*header.height) {
583            
584             printf("Image has transparency information. Storing as DefineBitsJpeg3 Tag (jpeg+alpha)\n");
585
586             // we always use quality 100, since png2swf is expected to
587             // use more or less lossless compression
588             
589             swf_SetJPEGBits3(t, header.width, header.height, (RGBA*)data2, 100);
590             t->id = ST_DEFINEBITSJPEG3;
591         } 
592         else
593 #endif
594         {
595             swf_SetLosslessBits(t, header.width, header.height, data2, BMF_32BIT);
596         }
597         free(data2);
598     } else if(header.mode == 0 || header.mode == 3) {
599         RGBA*rgba;
600         int swf_width = BYTES_PER_SCANLINE(header.width);
601         U8*data2 = malloc(swf_width*header.height);
602         U8*tmpline = malloc(header.width);
603         int i,x,y;
604         int pos=0;
605         if(header.mode == 3) { // palette or grayscale?
606             rgba = (RGBA*)malloc(palettelen*sizeof(RGBA));
607             if(!palette) {
608                 fprintf(stderr, "Error: No palette found!\n");
609                 exit(1);
610             }
611             /* 24->32 bit conversion */
612             for(i=0;i<palettelen;i++) {
613                 rgba[i].r = palette[i*3+0];
614                 rgba[i].g = palette[i*3+1];
615                 rgba[i].b = palette[i*3+2];
616                 if(alphapalette && i<alphapalettelen) {
617                     rgba[i].a = alphapalette[i];
618                     if(alphapalette[i] == 0) {
619                         /* if the color is fully transparent, it doesn't matter
620                            what it's rgb values are. furthermore, all Flash 
621                            players up to Flash 5 can't deal with anything beyond
622                            one transparent color with value (00,00,00,00). */
623                         rgba[i].r = rgba[i].g = rgba[i].b = 0;
624                     }
625                 } else {
626                     rgba[i].a = 255;
627                 }
628             }
629         } else {
630             palettelen = 256;
631             rgba = (RGBA*)malloc(palettelen*sizeof(RGBA));
632             for(i=0;i<256;i++) {
633                 rgba[i].r = i;
634                 rgba[i].g = i;
635                 rgba[i].b = i;
636             }
637         }
638
639         for(y=0;y<header.height;y++) {
640             int mode = imagedata[pos++]; //filter mode
641             U8*old;
642             U8*dest = &data2[y*swf_width];
643             U8*src;
644             src = &imagedata[pos];
645             if(header.bpp == 8) {
646                 pos+=header.width;
647             } else {
648                 int x,s=0;
649                 int bitpos = 0;
650                 U32 v = (1<<header.bpp)-1;
651                 for(x=0;x<header.width;x++) {
652                     U32 r = src[s/8]<<8 | 
653                             src[s/8+1];
654                     int t;
655                     tmpline[x] = (r>>(16-header.bpp-(s&7)))&v;
656                     s+=header.bpp;
657                 }
658                 src = tmpline;
659                 pos+=(header.width*header.bpp+7)/8;
660             }
661
662             if(!y) {
663                 memset(data2,0,swf_width);
664                 old = &data2[y*swf_width];
665             } else {
666                 old = &data2[(y-1)*swf_width];
667             }
668             applyfilter1(mode, src, old, dest, header.width);
669         }
670         swf_SetLosslessBitsIndexed(t, header.width, header.height, data2, rgba, palettelen);
671         free(tmpline);
672         free(rgba);
673         free(data2);
674     }
675
676     t = swf_InsertTag(t, ST_DEFINESHAPE3);
677
678     swf_ShapeNew(&s);
679     swf_GetMatrix(NULL, &m);
680     m.sx = 20 * 0x10000;
681     m.sy = 20 * 0x10000;
682     fs = swf_ShapeAddBitmapFillStyle(s, &m, id, 0);
683
684     swf_SetU16(t, id + 1);      // id
685
686     r.xmin = r.ymin = 0;
687     r.xmax = header.width * 20;
688     r.ymax = header.height * 20;
689     swf_SetRect(t, &r);
690
691     swf_SetShapeHeader(t, s);
692
693     swf_ShapeSetAll(t, s, 0, 0, 0, fs, 0);
694     swf_ShapeSetLine(t, s, r.xmax, 0);
695     swf_ShapeSetLine(t, s, 0, r.ymax);
696     swf_ShapeSetLine(t, s, -r.xmax, 0);
697     swf_ShapeSetLine(t, s, 0, -r.ymax);
698
699     swf_ShapeSetEnd(t);
700
701     t = swf_InsertTag(t, ST_REMOVEOBJECT2);
702     swf_SetU16(t, 50);          // depth
703
704     t = swf_InsertTag(t, ST_PLACEOBJECT2);
705
706     swf_GetMatrix(NULL, &m);
707     m.tx = (swf->movieSize.xmax - (int) header.width * 20) / 2;
708     m.ty = (swf->movieSize.ymax - (int) header.height * 20) / 2;
709     swf_ObjectPlace(t, id + 1, 50, &m, NULL, NULL);
710
711     t = swf_InsertTag(t, ST_SHOWFRAME);
712
713     fclose(fi);
714
715     return t;
716 }
717
718
719 int CheckInputFile(char *fname, char **realname)
720 {
721     FILE *fi;
722     char *s = malloc(strlen(fname) + 5);
723     struct png_header head;
724
725     if (!s)
726         exit(2);
727     (*realname) = s;
728     strcpy(s, fname);
729
730     // Check whether file exists (with typical extensions)
731
732     if ((fi = fopen(s, "rb")) == NULL) {
733         sprintf(s, "%s.png", fname);
734         if ((fi = fopen(s, "rb")) == NULL) {
735             sprintf(s, "%s.PNG", fname);
736             if ((fi = fopen(s, "rb")) == NULL) {
737                 sprintf(s, "%s.Png", fname);
738                 if ((fi = fopen(s, "rb")) == NULL)
739                     fprintf(stderr, "Couldn't open %s!\n", fname);
740                     return -1;
741             }
742         }
743     }
744
745     if(!png_read_header(fi, &head)) {
746         fprintf(stderr, "%s is not a PNG file!\n", fname);
747         return -1;
748     }
749
750     if (global.max_image_width < head.width)
751         global.max_image_width = head.width;
752     if (global.max_image_height < head.height)
753         global.max_image_height = head.height;
754
755     fclose(fi);
756
757     return 0;
758 }
759
760 int args_callback_option(char *arg, char *val)
761 {
762     int res = 0;
763     if (arg[1])
764         res = -1;
765     else
766         switch (arg[0]) {
767         case 'r':
768             if (val)
769                 global.framerate = atof(val);
770             if ((global.framerate < 1.0/256) ||(global.framerate >= 256.0)) {
771                 if (VERBOSE(1))
772                     fprintf(stderr,
773                             "Error: You must specify a valid framerate between 1/256 and 255.\n");
774                 exit(1);
775             }
776             res = 1;
777             break;
778
779         case 'o':
780             if (val)
781                 global.outfile = val;
782             res = 1;
783             break;
784
785         case 'v':
786             if (val)
787                 global.verbose = atoi(val);
788             res = 1;
789             break;
790
791         case 'X':
792             if (val)
793                 global.force_width = atoi(val);
794             res = 1;
795             break;
796
797         case 'Y':
798             if (val)
799                 global.force_height = atoi(val);
800             res = 1;
801             break;
802
803         case 'V':
804             printf("png2swf - part of %s %s\n", PACKAGE, VERSION);
805             exit(0);
806
807         default:
808             res = -1;
809             break;
810         }
811
812     if (res < 0) {
813         if (VERBOSE(1))
814             fprintf(stderr, "Unknown option: -%s\n", arg);
815         exit(1);
816         return 0;
817     }
818     return res;
819 }
820
821 struct options_t options[] = 
822
823 {"h", "help"},
824 {"o", "output"},
825 {"r", "rate"},
826 {"v", "verbose"},
827 {"X", "width"},
828 {"Y", "height"},
829 {"V", "version"},
830 {0,0}
831 };
832
833 int args_callback_longoption(char *name, char *val)
834 {
835     return args_long2shortoption(options, name, val);
836 }
837
838 int args_callback_command(char *arg, char *next)        // actually used as filename
839 {
840     char *s;
841     if (CheckInputFile(arg, &s) < 0) {
842         if (VERBOSE(1))
843             fprintf(stderr, "Error opening input file: %s\n", arg);
844         free(s);
845     } else {
846         image[global.nfiles].filename = s;
847         global.nfiles++;
848         if (global.nfiles >= MAX_INPUT_FILES) {
849             if (VERBOSE(1))
850                 fprintf(stderr, "Error: Too many input files.\n");
851             exit(1);
852         }
853     }
854     return 0;
855 }
856
857 void args_callback_usage(char *name)
858 {
859     printf("Usage: %s  [-options [value]] imagefiles[.png] [...]\n", name);
860     printf("-r framerate          (rate) Set movie framerate (frames per second)\n");
861     printf("-o outputfile         (output) Set name for SWF output file\n");
862     printf("-X pixel              (width) Force movie width to pixel (default: autodetect)\n");
863     printf("-Y pixel              (height) Force movie height to pixel (default: autodetect)\n");
864     printf("-v level              (verbose) Set verbose level (0=quiet, 1=default, 2=debug)\n");
865     printf("-V                    (version) Print version information and exit\n");
866 }
867
868
869 int main(int argc, char **argv)
870 {
871     SWF swf;
872     TAG *t;
873
874     memset(&global, 0x00, sizeof(global));
875
876     global.framerate = 1.0;
877     global.verbose = 1;
878
879     processargs(argc, argv);
880
881     if(global.nfiles<=0)
882         return 1;
883
884     if (VERBOSE(2))
885         fprintf(stderr, "Processing %i file(s)...\n", global.nfiles);
886
887     t = MovieStart(&swf, global.framerate,
888                    global.force_width ? global.force_width : global.
889                    max_image_width,
890                    global.force_height ? global.force_height : global.
891                    max_image_height);
892
893     {
894         int i;
895         for (i = 0; i < global.nfiles; i++) {
896             if (VERBOSE(3))
897                 fprintf(stderr, "[%03i] %s\n", i,
898                         image[i].filename);
899             t = MovieAddFrame(&swf, t, image[i].filename, (i * 2) + 1);
900             free(image[i].filename);
901         }
902     }
903
904     MovieFinish(&swf, t, global.outfile);
905
906     return 0;
907 }