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