added -c and -m options
[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     int do_cgi;
42     int version;
43     char *outfile;
44 } global;
45
46 struct {
47     char *filename;
48 } image[MAX_INPUT_FILES];
49
50 static int custom_move=0;
51 static int move_x=0;
52 static int move_y=0;
53 static int clip_x1=0,clip_y1=0,clip_x2=0,clip_y2=0;
54 static int custom_clip = 0;
55
56 TAG *MovieStart(SWF * swf, float framerate, int dx, int dy)
57 {
58     TAG *t;
59     RGBA rgb;
60
61     memset(swf, 0x00, sizeof(SWF));
62
63     swf->fileVersion = global.version;
64     swf->frameRate = (int)(256.0 * framerate);
65     if(custom_clip) {
66         swf->movieSize.xmin = clip_x1 * 20;
67         swf->movieSize.ymin = clip_y1 * 20;
68         swf->movieSize.xmax = clip_x2 * 20;
69         swf->movieSize.ymax = clip_y2 * 20;
70     } else {
71         swf->movieSize.xmax = dx * 20;
72         swf->movieSize.ymax = dy * 20;
73     }
74
75     t = swf->firstTag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
76
77     rgb.r = rgb.g = rgb.b = rgb.a = 0x00;
78     //rgb.g = 0xff; //<--- handy for testing alpha conversion
79     swf_SetRGB(t, &rgb);
80
81     return t;
82 }
83
84 int MovieFinish(SWF * swf, TAG * t, char *sname)
85 {
86     int f, so = fileno(stdout);
87     t = swf_InsertTag(t, ST_END);
88
89     if ((!isatty(so)) && (!sname))
90         f = so;
91     else {
92         if (!sname)
93             sname = "output.swf";
94         f = open(sname,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
95     }
96
97     if(global.do_cgi) {
98         if FAILED(swf_WriteCGI(swf)) fprintf(stderr,"WriteCGI() failed.\n");
99     } else {
100         if(global.version >= 6) {
101             if (swf_WriteSWC(f, swf)<0) 
102                     fprintf(stderr, "Unable to write output file: %s\n", sname);
103         } else {
104             if (swf_WriteSWF(f, swf)<0) 
105                     fprintf(stderr, "Unable to write output file: %s\n", sname);
106         }
107         if (f != so)
108             close(f);
109     }
110
111     swf_FreeTags(swf);
112     return 0;
113 }
114
115 int png_read_chunk(char (*head)[4], int*destlen, U8**destdata, FILE*fi)
116 {
117     unsigned int len;
118     if(destlen) *destlen=0;
119     if(destdata) *destdata=0;
120     if(!fread(&len, 4, 1, fi))
121         return 0;
122     if(!fread(head, 4, 1, fi))
123         return 0;
124     len = REVERSESWAP32(len);
125     if(destlen) *destlen = len;
126     if(destdata) {
127         if(len)
128             *destdata = malloc(len);
129         else 
130             *destdata = 0;
131         if(!fread(*destdata, len, 1, fi)) {
132             *destdata = 0;
133             if(destlen) *destlen=0;
134             return 0;
135         }
136         fseek(fi, 4, SEEK_CUR);
137
138     } else {
139         fseek(fi, len+4, SEEK_CUR);
140     }
141     return 1;
142 }
143
144 unsigned int png_get_dword(FILE*fi)
145 {
146     unsigned int a;
147     fread(&a,4,1,fi);
148     return REVERSESWAP32(a);
149 }
150
151 struct png_header
152 {
153     int width;
154     int height;
155     int bpp;
156     int mode;
157 };
158
159 int png_read_header(FILE*fi, struct png_header*header)
160 {
161     char id[4];
162     int len;
163     int ok=0;
164     U8 head[8] = {137,80,78,71,13,10,26,10};
165     U8 head2[8];
166     U8*data;
167     fread(head2,8,1,fi);
168     if(strncmp(head,head2,4))
169         return 0;
170    
171     while(png_read_chunk(&id, &len, &data, fi))
172     {
173         if(VERBOSE(2))
174             printf("%c%c%c%c %d\n", id[0],id[1],id[2],id[3],len);
175         if(!strncasecmp(id, "IHDR", 4)) {
176             char a,b,c,f,i;
177             if(len < 8) exit(1);
178             header->width = REVERSESWAP32(*(U32*)&data[0]);
179             header->height = REVERSESWAP32(*(U32*)&data[4]);
180             a = data[8];      // should be 8
181             b = data[9];      // should be 3(indexed) or 2(rgb)
182
183             c = data[10];     // compression mode (0)
184             f = data[11];     // filter mode (0)
185             i = data[12];     // interlace mode (0)
186         
187             if(VERBOSE(2)) printf("image mode:%d\n", b);
188             if(VERBOSE(2)) printf("bpp: %d\n", a);
189             if(VERBOSE(2)) printf("compression: %d\n", c);
190             if(VERBOSE(2)) printf("filter: %d\n", f);
191             if(VERBOSE(2)) printf("interlace: %d\n", i);
192
193             if(b!=0 && b!=2 && b!=3 && b!=6) {
194                 fprintf(stderr, "Image mode %d not supported!\n", b);
195                 if(b == 4) {
196                     fprintf(stderr, "(This is a grayscale image with alpha channel-\n");
197                     fprintf(stderr, " try converting it into an RGB image with alpha channel)\n");
198                 }
199                 exit(1);
200             }
201             if(a!=8 && (b==2 || b==6)) {
202                 fprintf(stderr, "Bpp %d in mode %d not supported!\n", a);
203                 exit(1);
204             }
205             if(c!=0) {
206                 fprintf(stderr, "Compression mode %d not supported!\n", c);
207                 exit(1);
208             }
209             if(f!=0) {
210                 fprintf(stderr, "Filter mode %d not supported!\n", f);
211                 exit(1);
212             }
213             if(i!=0) {
214                 fprintf(stderr, "Interlace mode %d not supported!\n", i);
215                 exit(1);
216             }
217             if(VERBOSE(2))
218                 printf("%dx%d %d %d %d %d %d\n",header->width, header->height, a,b,c,f,i);
219             header->bpp = a;
220             header->mode = b;
221             ok = 1;
222         } 
223         
224         free(data);
225     }
226     return ok;
227 }
228
229 typedef unsigned char byte;
230 #define ABS(a) ((a)>0?(a):(-(a)))
231 byte inline PaethPredictor (byte a,byte b,byte c)
232 {
233         // a = left, b = above, c = upper left
234         int p = a + b - c;        // initial estimate
235         int pa = ABS(p - a);      // distances to a, b, c
236         int pb = ABS(p - b);
237         int pc = ABS(p - c);
238         // return nearest of a,b,c,
239         // breaking ties in order a,b,c.
240         if (pa <= pb && pa <= pc) 
241                 return a;
242         else if (pb <= pc) 
243                 return b;
244         else return c;
245 }
246
247 void applyfilter3(int mode, U8*src, U8*old, U8*dest, int width)
248 {
249     int x;
250     unsigned char lastr=0;
251     unsigned char lastg=0;
252     unsigned char lastb=0;
253     unsigned char upperlastr=0;
254     unsigned char upperlastg=0;
255     unsigned char upperlastb=0;
256
257     if(mode==0) {
258         for(x=0;x<width;x++) {
259             dest[0] = 255;
260             dest[1] = src[0];
261             dest[2] = src[1];
262             dest[3] = src[2];
263             dest+=4;
264             src+=3;
265         }
266     }
267     else if(mode==1) {
268         for(x=0;x<width;x++) {
269             dest[0] = 255;
270             dest[1] = src[0]+lastr;
271             dest[2] = src[1]+lastg;
272             dest[3] = src[2]+lastb;
273             lastr = dest[1];
274             lastg = dest[2];
275             lastb = dest[3];
276             dest+=4;
277             src+=3;
278         }
279     }
280     else if(mode==2) {
281         for(x=0;x<width;x++) {
282             dest[0] = 255;
283             dest[1] = src[0]+old[1];
284             dest[2] = src[1]+old[2];
285             dest[3] = src[2]+old[3];
286             dest+=4;
287             old+=4;
288             src+=3;
289         }
290     }
291     else if(mode==3) {
292         for(x=0;x<width;x++) {
293             dest[0] = 255;
294             dest[1] = src[0]+(old[1]+lastr)/2;
295             dest[2] = src[1]+(old[2]+lastg)/2;
296             dest[3] = src[2]+(old[3]+lastb)/2;
297             lastr = dest[1];
298             lastg = dest[2];
299             lastb = dest[3];
300             dest+=4;
301             old+=4;
302             src+=3;
303         }
304     }
305     else if(mode==4) {
306         for(x=0;x<width;x++) {
307             dest[0] = 255;
308             dest[1] = src[0]+PaethPredictor(lastr,old[1],upperlastr);
309             dest[2] = src[1]+PaethPredictor(lastg,old[2],upperlastg);
310             dest[3] = src[2]+PaethPredictor(lastb,old[3],upperlastb);
311             lastr = dest[1];
312             lastg = dest[2];
313             lastb = dest[3];
314             upperlastr = old[1];
315             upperlastg = old[2];
316             upperlastb = old[3];
317             dest+=4;
318             old+=4;
319             src+=3;
320         }
321     }    
322
323 }
324
325 void applyfilter4(int mode, U8*src, U8*old, U8*dest, int width)
326 {
327     int x;
328     unsigned char lastr=0;
329     unsigned char lastg=0;
330     unsigned char lastb=0;
331     unsigned char lasta=0; //TODO: 255?
332     unsigned char upperlastr=0;
333     unsigned char upperlastg=0;
334     unsigned char upperlastb=0;
335     unsigned char upperlasta=0; //TODO: 255?
336
337     if(mode==0) {
338         for(x=0;x<width;x++) {
339             dest[0] = src[3];
340             dest[1] = src[0];
341             dest[2] = src[1];
342             dest[3] = src[2];
343             dest+=4;
344             src+=4;
345         }
346     }
347     else if(mode==1) {
348         for(x=0;x<width;x++) {
349             dest[0] = src[3]+lasta;
350             dest[1] = src[0]+lastr;
351             dest[2] = src[1]+lastg;
352             dest[3] = src[2]+lastb;
353             lasta = dest[0];
354             lastr = dest[1];
355             lastg = dest[2];
356             lastb = dest[3];
357             dest+=4;
358             src+=4;
359         }
360     }
361     else if(mode==2) {
362         for(x=0;x<width;x++) {
363             dest[0] = src[3]+old[0];
364             dest[1] = src[0]+old[1];
365             dest[2] = src[1]+old[2];
366             dest[3] = src[2]+old[3];
367             dest+=4;
368             old+=4;
369             src+=4;
370         }
371     }
372     else if(mode==3) {
373         for(x=0;x<width;x++) {
374             dest[0] = src[3]+(old[0]+lasta)/2;
375             dest[1] = src[0]+(old[1]+lastr)/2;
376             dest[2] = src[1]+(old[2]+lastg)/2;
377             dest[3] = src[2]+(old[3]+lastb)/2;
378             lasta = dest[0];
379             lastr = dest[1];
380             lastg = dest[2];
381             lastb = dest[3];
382             dest+=4;
383             old+=4;
384             src+=4;
385         }
386     }
387     else if(mode==4) {
388         for(x=0;x<width;x++) {
389             dest[0] = src[3]+PaethPredictor(lasta,old[0],upperlasta);
390             dest[1] = src[0]+PaethPredictor(lastr,old[1],upperlastr);
391             dest[2] = src[1]+PaethPredictor(lastg,old[2],upperlastg);
392             dest[3] = src[2]+PaethPredictor(lastb,old[3],upperlastb);
393             lasta = dest[0];
394             lastr = dest[1];
395             lastg = dest[2];
396             lastb = dest[3];
397             upperlasta = old[0];
398             upperlastr = old[1];
399             upperlastg = old[2];
400             upperlastb = old[3];
401             dest+=4;
402             old+=4;
403             src+=4;
404         }
405     }    
406
407 }
408
409 void applyfilter1(int mode, U8*src, U8*old, U8*dest, int width)
410 {
411     int x;
412     unsigned char last=0;
413     unsigned char upperlast=0;
414
415     if(mode==0) {
416         for(x=0;x<width;x++) {
417             *dest = *src;
418             dest++;
419             src++;
420         }
421     }
422     else if(mode==1) {
423         for(x=0;x<width;x++) {
424             *dest = *src+last;
425             last = *dest;
426             dest++;
427             src++;
428         }
429     }
430     else if(mode==2) {
431         for(x=0;x<width;x++) {
432             *dest = *src+*old;
433             dest++;
434             old++;
435             src++;
436         }
437     }
438     else if(mode==3) {
439         for(x=0;x<width;x++) {
440             *dest = *src+(*old+last)/2;
441             last = *dest;
442             dest++;
443             old++;
444             src++;
445         }
446     }
447     else if(mode==4) {
448         for(x=0;x<width;x++) {
449             *dest = *src+PaethPredictor(last,*old,upperlast);
450             last = *dest;
451             upperlast = *old;
452             dest++;
453             old++;
454             src++;
455         }
456     }    
457
458 }
459
460 TAG *MovieAddFrame(SWF * swf, TAG * t, char *sname, int id)
461 {
462     SHAPE *s;
463     SRECT r;
464     MATRIX m;
465     int fs;
466
467     char tagid[4];
468     int len;
469     U8*data;
470     U8*imagedata;
471     U8*zimagedata=0;
472     unsigned long int imagedatalen;
473     unsigned long int zimagedatalen=0;
474     U8*palette = 0;
475     int palettelen = 0;
476     U8*alphapalette = 0;
477     int alphapalettelen = 0;
478     struct png_header header;
479     int bypp;
480
481     FILE *fi;
482     U8 *scanline;
483
484     if ((fi = fopen(sname, "rb")) == NULL) {
485         if (VERBOSE(1))
486             fprintf(stderr, "Read access failed: %s\n", sname);
487         return t;
488     }
489
490     if(!png_read_header(fi, &header))
491         return 0;
492
493     if(header.mode == 3 || header.mode == 0) bypp = 1;
494     else
495     if(header.mode == 2) bypp = 3;
496     else
497     if(header.mode == 6) bypp = 4;
498     else
499         return 0;
500     imagedatalen = bypp * header.width * header.height + 65536;
501     imagedata = malloc(imagedatalen);
502
503     fseek(fi,8,SEEK_SET);
504     while(!feof(fi))
505     {
506         if(!png_read_chunk(&tagid, &len, &data, fi))
507             break;
508         if(!strncmp(tagid, "IEND", 4)) {
509             break;
510         }
511         if(!strncmp(tagid, "PLTE", 4)) {
512             palette = data;
513             palettelen = len/3;
514             data = 0; //don't free data
515             if(VERBOSE(2))
516                 printf("%d colors in palette\n", palettelen);
517         }
518         if(!strncmp(tagid, "tRNS", 4)) {
519             if(header.mode == 3) {
520                 alphapalette = data;
521                 alphapalettelen = len;
522                 data = 0; //don't free data
523                 if(VERBOSE(2))
524                     printf("found %d alpha colors\n", alphapalettelen);
525             }
526         }
527         if(!strncmp(tagid, "IDAT", 4)) {
528             if(!zimagedata) {
529                 zimagedatalen = len;
530                 zimagedata = malloc(len);
531                 memcpy(zimagedata,data,len);
532             } else {
533                 zimagedata = realloc(zimagedata, zimagedatalen+len);
534                 memcpy(&zimagedata[zimagedatalen], data, len);
535                 zimagedatalen += len;
536             }
537         }
538         if(data)
539             free(data);
540     }
541     
542     if(!zimagedata || uncompress(imagedata, &imagedatalen, zimagedata, zimagedatalen) != Z_OK) {
543         fprintf(stderr, "Couldn't uncompress %s!\n", sname);
544         if(zimagedata)
545             free(zimagedata);
546         return 0;
547     }
548     free(zimagedata);
549
550     if(alphapalette)
551         t = swf_InsertTag(t, ST_DEFINEBITSLOSSLESS2);
552     else
553         t = swf_InsertTag(t, ST_DEFINEBITSLOSSLESS);
554
555     swf_SetU16(t, id);          // id
556     if(header.mode == 2 || header.mode == 6) {
557         U8*data2 = malloc(header.width*header.height*4);
558         int i,s=0;
559         int x,y;
560         int pos=0;
561         int opaque=0;
562         int transparent=0;
563         int semitransparent=0;
564         /* in case for mode 2, the following also performs 24->32 bit conversion */
565         unsigned char* firstline = malloc(header.width*4);
566
567         for(y=0;y<header.height;y++) {
568             int mode = imagedata[pos++]; //filter mode
569             U8*src;
570             U8*dest;
571             U8*old;
572             dest = &data2[(y*header.width)*4];
573
574             if(header.bpp == 8)
575             {
576                 /* one byte per pixel */
577                 src = &imagedata[pos];
578                 pos+=header.width*(header.mode==6?4:3);
579             } else {
580                 /* not implemented yet */
581                 exit(1);
582             }
583
584             if(!y) {
585                 old = firstline;
586                 memset(old, 0, header.width*4); //TODO: fill alpha with 255?
587             } else {
588                 old = &data2[(y-1)*header.width*4];
589             }
590             if(header.mode==6)
591                 applyfilter4(mode, src, old, dest, header.width);
592             else if(header.mode==2)
593                 applyfilter3(mode, src, old, dest, header.width);
594         }
595         free(firstline);
596
597         /* the image is now compressed and stored in data. Now let's take
598            a look at the alpha values */
599         if(header.mode == 6) {
600             for(y=0;y<header.height;y++) {
601                 U8*l = &data2[(y*header.width)*4];
602                 for(x=0;x<header.width;x++) {
603                     U8 a = l[x*4];
604                     U8 b = l[x*4+1];
605                     U8 g = l[x*4+2];
606                     U8 r = l[x*4+3];
607                     if(a==255) transparent++;
608                     else {
609                         if(a==0) opaque++;
610                         else semitransparent++;
611                         l[x*4+3]=(int)r*a/255;
612                         l[x*4+2]=(int)g*a/255;
613                         l[x*4+1]=(int)b*a/255;
614                     }
615                 }
616             }
617             if(semitransparent || opaque) {
618                 t->id = ST_DEFINEBITSLOSSLESS2;
619             }
620         }
621         swf_SetLosslessBits(t, header.width, header.height, data2, BMF_32BIT);
622         free(data2);
623     } else if(header.mode == 0 || header.mode == 3) {
624         RGBA*rgba;
625         int swf_width = BYTES_PER_SCANLINE(header.width);
626         U8*data2 = malloc(swf_width*header.height);
627         U8*tmpline = malloc(header.width);
628         int i,x,y;
629         int pos=0;
630         if(header.mode == 3) { // palette or grayscale?
631             rgba = (RGBA*)malloc(palettelen*sizeof(RGBA));
632             if(!palette) {
633                 fprintf(stderr, "Error: No palette found!\n");
634                 exit(1);
635             }
636             /* 24->32 bit conversion */
637             for(i=0;i<palettelen;i++) {
638                 rgba[i].r = palette[i*3+0];
639                 rgba[i].g = palette[i*3+1];
640                 rgba[i].b = palette[i*3+2];
641                 if(alphapalette && i<alphapalettelen) {
642                     rgba[i].a = alphapalette[i];
643                     rgba[i].r = ((int)rgba[i].r*rgba[i].a)/255;
644                     rgba[i].g = ((int)rgba[i].g*rgba[i].a)/255;
645                     rgba[i].b = ((int)rgba[i].b*rgba[i].a)/255;
646                 } else {
647                     rgba[i].a = 255;
648                 }
649             }
650         } else {
651             int mult = (0x1ff>>header.bpp);
652             palettelen = 1<<header.bpp;
653             rgba = (RGBA*)malloc(palettelen*sizeof(RGBA));
654             for(i=0;i<palettelen;i++) {
655                 rgba[i].r = i*mult;
656                 rgba[i].g = i*mult;
657                 rgba[i].b = i*mult;
658             }
659         }
660
661         for(y=0;y<header.height;y++) {
662             int mode = imagedata[pos++]; //filter mode
663             U8*old;
664             U8*dest = &data2[y*swf_width];
665             U8*src;
666             src = &imagedata[pos];
667             if(header.bpp == 8) {
668                 pos+=header.width;
669             } else {
670                 int x,s=0;
671                 int bitpos = 0;
672                 U32 v = (1<<header.bpp)-1;
673                 for(x=0;x<header.width;x++) {
674                     U32 r = src[s/8]<<8 | 
675                             src[s/8+1];
676                     int t;
677                     tmpline[x] = (r>>(16-header.bpp-(s&7)))&v;
678                     s+=header.bpp;
679                 }
680                 src = tmpline;
681                 pos+=(header.width*header.bpp+7)/8;
682             }
683
684             if(!y) {
685                 memset(data2,0,swf_width);
686                 old = &data2[y*swf_width];
687             } else {
688                 old = &data2[(y-1)*swf_width];
689             }
690             applyfilter1(mode, src, old, dest, header.width);
691         }
692         swf_SetLosslessBitsIndexed(t, header.width, header.height, data2, rgba, palettelen);
693         free(tmpline);
694         free(rgba);
695         free(data2);
696     }
697
698     t = swf_InsertTag(t, ST_DEFINESHAPE3);
699
700     swf_ShapeNew(&s);
701     swf_GetMatrix(NULL, &m);
702     m.sx = 20 * 0x10000;
703     m.sy = 20 * 0x10000;
704     m.tx = -10;
705     m.ty = -10;
706     fs = swf_ShapeAddBitmapFillStyle(s, &m, id, 1);
707
708     swf_SetU16(t, id + 1);      // id
709
710     r.xmin = r.ymin = 0;
711     r.xmax = header.width * 20;
712     r.ymax = header.height * 20;
713     swf_SetRect(t, &r);
714
715     swf_SetShapeHeader(t, s);
716
717     swf_ShapeSetAll(t, s, 0, 0, 0, fs, 0);
718     swf_ShapeSetLine(t, s, r.xmax, 0);
719     swf_ShapeSetLine(t, s, 0, r.ymax);
720     swf_ShapeSetLine(t, s, -r.xmax, 0);
721     swf_ShapeSetLine(t, s, 0, -r.ymax);
722
723     swf_ShapeSetEnd(t);
724
725     t = swf_InsertTag(t, ST_REMOVEOBJECT2);
726     swf_SetU16(t, 50);          // depth
727
728     t = swf_InsertTag(t, ST_PLACEOBJECT2);
729
730     swf_GetMatrix(NULL, &m);
731     if(custom_move) {
732         m.tx = move_x*20;
733         m.ty = move_y*20;
734     } else {
735         m.tx = (swf->movieSize.xmax - (int) header.width * 20) / 2;
736         m.ty = (swf->movieSize.ymax - (int) header.height * 20) / 2;
737     }
738     swf_ObjectPlace(t, id + 1, 50, &m, NULL, NULL);
739
740     t = swf_InsertTag(t, ST_SHOWFRAME);
741
742     fclose(fi);
743
744     return t;
745 }
746
747
748 int CheckInputFile(char *fname, char **realname)
749 {
750     FILE *fi;
751     char *s = malloc(strlen(fname) + 5);
752     struct png_header head;
753
754     if (!s)
755         exit(2);
756     (*realname) = s;
757     strcpy(s, fname);
758
759     // Check whether file exists (with typical extensions)
760
761     if ((fi = fopen(s, "rb")) == NULL) {
762         sprintf(s, "%s.png", fname);
763         if ((fi = fopen(s, "rb")) == NULL) {
764             sprintf(s, "%s.PNG", fname);
765             if ((fi = fopen(s, "rb")) == NULL) {
766                 sprintf(s, "%s.Png", fname);
767                 if ((fi = fopen(s, "rb")) == NULL) {
768                     fprintf(stderr, "Couldn't open %s!\n", fname);
769                     return -1;
770                 }
771             }
772         }
773     }
774
775     if(!png_read_header(fi, &head)) {
776         fprintf(stderr, "%s is not a PNG file!\n", fname);
777         return -1;
778     }
779
780     if (global.max_image_width < head.width)
781         global.max_image_width = head.width;
782     if (global.max_image_height < head.height)
783         global.max_image_height = head.height;
784
785     fclose(fi);
786
787     return 0;
788 }
789
790 int args_callback_option(char *arg, char *val)
791 {
792     int res = 0;
793     if (arg[1])
794         res = -1;
795     else
796         switch (arg[0]) {
797         case 'r':
798             if (val)
799                 global.framerate = atof(val);
800             /* removed framerate>0 restriction in order to make
801                Flash Communication Server compatible SWFs */
802             if ((global.framerate < 0) ||(global.framerate >= 256.0)) {
803                 if (VERBOSE(1))
804                     fprintf(stderr,
805                             "Error: You must specify a valid framerate between 1/256 and 255.\n");
806                 exit(1);
807             }
808             res = 1;
809             break;
810
811         case 'o':
812             if (val)
813                 global.outfile = val;
814             res = 1;
815             break;
816
817         case 'z':
818             global.version = 6;
819             res = 0;
820             break;
821
822         case 'T':
823             global.version = atoi(val);
824             res = 1;
825             break;
826
827         case 'C':
828             global.do_cgi = 1;
829             break;
830
831         case 'v':
832             global.verbose++;
833             res = 0;
834             break;
835
836         case 'q':
837             global.verbose--;
838             if(global.verbose<0)
839                 global.verbose = 0;
840             res = 0;
841             break;
842
843         case 'X':
844             if (val)
845                 global.force_width = atoi(val);
846             res = 1;
847             break;
848
849         case 'Y':
850             if (val)
851                 global.force_height = atoi(val);
852             res = 1;
853             break;
854
855         case 'V':
856             printf("png2swf - part of %s %s\n", PACKAGE, VERSION);
857             exit(0);
858    
859         case 'c': {
860             char*s = strdup(val);
861             char*x1 = strtok(s, ":");
862             char*y1 = strtok(0, ":");
863             char*x2 = strtok(0, ":");
864             char*y2 = strtok(0, ":");
865             if(!(x1 && y1 && x2 && y2)) {
866                 fprintf(stderr, "-m option requires four arguments, <x1>:<y1>:<x2>:<y2>\n");
867                 exit(1);
868             }
869             custom_clip = 1;
870             clip_x1 = atoi(x1);
871             clip_y1 = atoi(y1);
872             clip_x2 = atoi(x2);
873             clip_y2 = atoi(y2);
874             free(s);
875
876             res = 1;
877             break;
878         }
879
880         case 'm': {
881             char*s = strdup(val);
882             char*c = strchr(s, ':');
883             if(!c) {
884                 fprintf(stderr, "-m option requires two arguments, <x>:<y>\n");
885                 exit(1);
886             }
887             *c = 0;
888             custom_move = 1;
889             move_x = atoi(val);
890             move_y = atoi(c+1);
891             free(s);
892
893             res = 1;
894             break;
895         }
896
897         default:
898             res = -1;
899             break;
900         }
901
902     if (res < 0) {
903         if (VERBOSE(1))
904             fprintf(stderr, "Unknown option: -%s\n", arg);
905         exit(1);
906         return 0;
907     }
908     return res;
909 }
910
911 static struct options_t options[] = {
912 {"r", "rate"},
913 {"o", "output"},
914 {"z", "zlib"},
915 {"X", "pixel"},
916 {"Y", "pixel"},
917 {"v", "verbose"},
918 {"q", "quiet"},
919 {"C", "cgi"},
920 {"V", "version"},
921 {0,0}
922 };
923
924 int args_callback_longoption(char *name, char *val)
925 {
926     return args_long2shortoption(options, name, val);
927 }
928
929 int args_callback_command(char *arg, char *next)        // actually used as filename
930 {
931     char *s;
932     if (CheckInputFile(arg, &s) < 0) {
933         if (VERBOSE(1))
934             fprintf(stderr, "Error opening input file: %s\n", arg);
935         free(s);
936     } else {
937         image[global.nfiles].filename = s;
938         global.nfiles++;
939         if (global.nfiles >= MAX_INPUT_FILES) {
940             if (VERBOSE(1))
941                 fprintf(stderr, "Error: Too many input files.\n");
942             exit(1);
943         }
944     }
945     return 0;
946 }
947
948 void args_callback_usage(char *name)
949 {
950     printf("\n");
951     printf("Usage: %s [-X width] [-Y height] [-o file.swf] [-r rate] file1.png [file2.png...]\n", name);
952     printf("\n");
953     printf("-r , --rate <framerate>        Set movie framerate (frames per second)\n");
954     printf("-o , --output <filename>       Set name for SWF output file.\n");
955     printf("-z , --zlib <zlib>             Enable Flash 6 (MX) Zlib Compression\n");
956     printf("-X , --pixel <width>           Force movie width to <width> (default: autodetect)\n");
957     printf("-Y , --pixel <height>          Force movie height to <height> (default: autodetect)\n");
958     printf("-v , --verbose                 Be verbose. Use more than one -v for greater effect \n");
959     printf("-q , --quiet                   Omit normal log messages, only log errors\n");
960     printf("-C , --cgi                     For use as CGI- prepend http header, write to stdout\n");
961     printf("-V , --version                 Print version information and exit\n");
962     printf("\n");
963 }
964
965 int main(int argc, char **argv)
966 {
967     SWF swf;
968     TAG *t;
969
970     memset(&global, 0x00, sizeof(global));
971
972     global.framerate = 1.0;
973     global.verbose = 1;
974     global.version = 4;
975
976     processargs(argc, argv);
977     
978     if(global.nfiles<=0) {
979         fprintf(stderr, "No png files found in arguments\n");
980         return 1;
981     }
982
983     if (VERBOSE(2))
984         fprintf(stderr, "Processing %i file(s)...\n", global.nfiles);
985
986     t = MovieStart(&swf, global.framerate,
987                    global.force_width ? global.force_width : global.
988                    max_image_width,
989                    global.force_height ? global.force_height : global.
990                    max_image_height);
991
992     {
993         int i;
994         for (i = 0; i < global.nfiles; i++) {
995             if (VERBOSE(3))
996                 fprintf(stderr, "[%03i] %s\n", i,
997                         image[i].filename);
998             t = MovieAddFrame(&swf, t, image[i].filename, (i * 2) + 1);
999             free(image[i].filename);
1000         }
1001     }
1002
1003     MovieFinish(&swf, t, global.outfile);
1004
1005     return 0;
1006 }