initial revision.
[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 Matthias Kramm <kramm@quiss.org>
8  
9    This file is distributed under the GPL, see file COPYING for details 
10
11 */
12
13 #include <stdio.h>
14 #include <math.h>
15 #include <fcntl.h>
16 #include <zlib.h>
17 #include "../lib/rfxswf.h"
18 #include "../lib/args.h"
19
20 #define MAX_INPUT_FILES 1024
21 #define VERBOSE(x) (global.verbose>=x)
22
23 struct {
24     int framerate;
25     int max_image_width;
26     int max_image_height;
27     int force_width;
28     int force_height;
29     int prescale;
30     int nfiles;
31     int verbose;
32     char *outfile;
33 } global;
34
35 struct {
36     char *filename;
37     int scale;
38 } image[MAX_INPUT_FILES];
39
40 TAG *MovieStart(SWF * swf, int framerate, int dx, int dy)
41 {
42     TAG *t;
43     RGBA rgb;
44
45     memset(swf, 0x00, sizeof(SWF));
46
47     swf->fileVersion = 4;
48     swf->frameRate = (25600 / framerate);
49     swf->movieSize.xmax = dx * 20;
50     swf->movieSize.ymax = dy * 20;
51
52     t = swf->firstTag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
53
54     rgb.r = rgb.g = rgb.b = rgb.a = 0x00;
55     swf_SetRGB(t, &rgb);
56
57     return t;
58 }
59
60 int MovieFinish(SWF * swf, TAG * t, char *sname)
61 {
62     int handle, so = fileno(stdout);
63     t = swf_InsertTag(t, ST_END);
64
65     if ((!isatty(so)) && (!sname))
66         handle = so;
67     else {
68         if (!sname)
69             sname = "output.swf";
70         handle = open(sname, O_RDWR | O_CREAT | O_TRUNC, 0666);
71     }
72     if FAILED
73         (swf_WriteSWF(handle, swf)) if (VERBOSE(1))
74             fprintf(stderr, "Unable to write output file: %s\n", sname);
75     if (handle != so)
76         close(handle);
77
78     swf_FreeTags(swf);
79     return 0;
80 }
81
82 int png_read_chunk(char (*head)[4], int*destlen, U8**destdata, FILE*fi)
83 {
84     unsigned int len;
85     if(destlen) *destlen=0;
86     if(destdata) *destdata=0;
87     if(!fread(&len, 4, 1, fi))
88         return 0;
89     if(!fread(head, 4, 1, fi))
90         return 0;
91     len = REVERSESWAP32(len);
92     printf("id: %.4s len: %d\n", head, len);
93     if(destlen) *destlen = len;
94     if(destdata) {
95         if(len)
96             *destdata = malloc(len);
97         else 
98             *destdata = 0;
99         if(!fread(*destdata, len, 1, fi)) {
100             *destdata = 0;
101             if(destlen) *destlen=0;
102             return 0;
103         }
104         fseek(fi, 4, SEEK_CUR);
105
106     } else {
107         fseek(fi, len+4, SEEK_CUR);
108     }
109     return 1;
110 }
111
112 unsigned int png_get_dword(FILE*fi)
113 {
114     unsigned int a;
115     fread(&a,4,1,fi);
116     return REVERSESWAP32(a);
117 }
118
119 struct png_header
120 {
121     int width;
122     int height;
123     int bpp;
124     int mode;
125 };
126
127 int png_read_header(FILE*fi, struct png_header*header)
128 {
129     char id[4];
130     int len;
131     U8 head[8] = {137,80,78,71,13,10,26,10};
132     U8 head2[8];
133     U8*data;
134     fread(head2,8,1,fi);
135     if(strncmp(head,head2,4))
136         return 0;
137    
138     while(png_read_chunk(&id, &len, &data, fi))
139     {
140         printf("%c%c%c%c %d\n", id[0],id[1],id[2],id[3],len);
141         if(!strncasecmp(id, "IHDR", 4)) {
142             char a,b,c,f,i;
143             if(len < 8) exit(1);
144             header->width = REVERSESWAP32(*(U32*)&data[0]);
145             header->height = REVERSESWAP32(*(U32*)&data[4]);
146             a = data[8];      // should be 8
147             b = data[9];      // should be 3(indexed) or 2(rgb)
148
149             c = data[10];     // compression mode (0)
150             f = data[11];     // filter mode (0)
151             i = data[12];     // interlace mode (0)
152             printf("%08xx%08x %d %d %d %d %d\n",header->width, header->height, a,b,c,f,i);
153             header->bpp = a;
154             header->mode = b;
155             return 1;
156         } else {
157             fseek(fi, len, SEEK_CUR);
158         }
159         free(data);
160     }
161     return 0;
162 }
163
164 TAG *MovieAddFrame(SWF * swf, TAG * t, char *sname, int scale,
165                    int id)
166 {
167     SHAPE *s;
168     SRECT r;
169     MATRIX m;
170     int fs;
171
172     char tagid[4];
173     int len;
174     U8*data;
175     U8*imagedata;
176     unsigned long int imagedatalen;
177     U8*palette;
178     int palettelen;
179     struct png_header header;
180     int bypp;
181
182     FILE *fi;
183     U8 *scanline;
184
185     if ((fi = fopen(sname, "rb")) == NULL) {
186         if (VERBOSE(1))
187             fprintf(stderr, "Read access failed: %s\n", sname);
188         return t;
189     }
190
191     if(!png_read_header(fi, &header))
192         return 0;
193
194     if(header.mode == 3) bypp = 1;
195     else
196     if(header.mode == 2) bypp = 3;
197     else
198         return 0;
199     imagedatalen = bypp * header.width * header.height + 65536;
200     imagedata = malloc(imagedatalen);
201
202     fseek(fi,8,SEEK_SET);
203     while(!feof(fi))
204     {
205         if(!png_read_chunk(&tagid, &len, &data, fi))
206             break;
207         if(!strncmp(tagid, "IEND", 4)) {
208             break;
209         }
210         if(!strncmp(tagid, "PLTE", 4)) {
211             palette = data;
212             palettelen = len/3;
213             data = 0;
214             printf("%d palette\n", len);
215         }
216         if(!strncmp(tagid, "IDAT", 4)) {
217             if(uncompress(imagedata, &imagedatalen, data, len) != Z_OK) {
218                 fprintf(stderr, "Couldn't uncompress %s!\n", sname);
219                 return 0;
220             }
221             printf("IDAT %d -> %d\n", len, imagedatalen);
222         }
223         if(data)
224             free(data);
225     }
226
227     t = swf_InsertTag(t, ST_DEFINEBITSLOSSLESS);
228     swf_SetU16(t, id);          // id
229     if(header.mode == 2) {
230         U8*data2 = malloc(header.width*header.height*4);
231         int i,s=0;
232         int x,y;
233         int pos=0;
234         /* 24->32 bit conversion */
235         for(y=0;y<header.height;y++) {
236             pos++; // filter mode
237             for(x=0;x<header.width;x++) {
238                 U8*src  = &imagedata[pos];
239                 U8*dest = &data2[(y*header.width+x)*4];
240                 dest[0] = 255;
241                 dest[1] = src[0];
242                 dest[2] = src[1];
243                 dest[3] = src[2];
244                 pos+=3;
245             }
246         }
247         swf_SetLosslessBits(t, header.width, header.height, data2, BMF_32BIT);
248         free(data2);
249     }
250     else {
251         RGBA*rgba = (RGBA*)malloc(palettelen*sizeof(RGBA));
252         U8*data2 = malloc((header.width+4)*header.height);
253         int i,x,y;
254         int pos=0;
255         /* 24->32 bit conversion */
256         for(i=0;i<palettelen;i++) {
257             rgba[i].r = palette[i*3+0];
258             rgba[i].g = palette[i*3+1];
259             rgba[i].b = palette[i*3+2];
260             rgba[i].a = 255;
261         }
262         for(y=0;y<header.height;y++) {
263             pos++; //filter mode
264             for(x=0;x<header.width;x++) {
265                 data2[y*header.width+x] = 
266                     imagedata[pos++];
267             }
268         }
269         swf_SetLosslessBitsIndexed(t, header.width, header.height, data2, rgba, palettelen);
270         free(rgba);
271         free(data2);
272     }
273
274     t = swf_InsertTag(t, ST_DEFINESHAPE);
275
276     swf_ShapeNew(&s);
277     swf_GetMatrix(NULL, &m);
278     m.sx = 20 * 0x10000;
279     m.sy = 20 * 0x10000;
280     fs = swf_ShapeAddBitmapFillStyle(s, &m, id, 0);
281
282     swf_SetU16(t, id + 1);      // id
283
284     r.xmin = r.ymin = 0;
285     r.xmax = header.width * 20;
286     r.ymax = header.height * 20;
287     swf_SetRect(t, &r);
288
289     swf_SetShapeHeader(t, s);
290
291     swf_ShapeSetAll(t, s, 0, 0, 0, fs, 0);
292     swf_ShapeSetLine(t, s, r.xmax, 0);
293     swf_ShapeSetLine(t, s, 0, r.ymax);
294     swf_ShapeSetLine(t, s, -r.xmax, 0);
295     swf_ShapeSetLine(t, s, 0, -r.ymax);
296
297     swf_ShapeSetEnd(t);
298
299     t = swf_InsertTag(t, ST_REMOVEOBJECT2);
300     swf_SetU16(t, 1);           // depth
301
302     t = swf_InsertTag(t, ST_PLACEOBJECT2);
303
304     swf_GetMatrix(NULL, &m);
305     m.tx = (swf->movieSize.xmax - (int) header.width * 20) / 2;
306     m.ty = (swf->movieSize.ymax - (int) header.height * 20) / 2;
307     swf_ObjectPlace(t, id + 1, 1, &m, NULL, NULL);
308
309     t = swf_InsertTag(t, ST_SHOWFRAME);
310
311     fclose(fi);
312
313     return t;
314 }
315
316
317 int CheckInputFile(char *fname, char **realname)
318 {
319     FILE *fi;
320     char *s = malloc(strlen(fname) + 5);
321     struct png_header head;
322
323     if (!s)
324         exit(2);
325     (*realname) = s;
326     strcpy(s, fname);
327
328     // Check whether file exists (with typical extensions)
329
330     if ((fi = fopen(s, "rb")) == NULL) {
331         sprintf(s, "%s.png", fname);
332         if ((fi = fopen(s, "rb")) == NULL) {
333             sprintf(s, "%s.PNG", fname);
334             if ((fi = fopen(s, "rb")) == NULL) {
335                 sprintf(s, "%s.Png", fname);
336                 if ((fi = fopen(s, "rb")) == NULL)
337                     fprintf(stderr, "Couldn't open %s!\n", fname);
338                     return -1;
339             }
340         }
341     }
342
343     if(!png_read_header(fi, &head)) {
344         fprintf(stderr, "%s is not a PNG file!\n", fname);
345         return -1;
346     }
347     if(head.mode!=2 && head.mode!=3) {
348         fprintf(stderr, "%s has unsupported mode %d\n", head.mode);
349         return -1;
350     }
351     if(head.bpp!=8) {
352         fprintf(stderr, "%s has unsupported bpp %d\n", head.bpp);
353         return -1;
354     }
355
356     if (global.max_image_width < head.width)
357         global.max_image_width = head.width;
358     if (global.max_image_height < head.height)
359         global.max_image_height = head.height;
360
361     fclose(fi);
362
363     return 0;
364 }
365
366 int args_callback_option(char *arg, char *val)
367 {
368     int res = 0;
369     if (arg[1])
370         res = -1;
371     else
372         switch (arg[0]) {
373         case 'r':
374             if (val)
375                 global.framerate = atoi(val);
376             if ((global.framerate < 1) ||(global.framerate > 5000)) {
377                 if (VERBOSE(1))
378                     fprintf(stderr,
379                             "Error: You must specify a valid framerate between 1 and 10000.\n");
380                 exit(1);
381             }
382             res = 1;
383             break;
384
385         case 'o':
386             if (val)
387                 global.outfile = val;
388             res = 1;
389             break;
390
391         case 'v':
392             if (val)
393                 global.verbose = atoi(val);
394             res = 1;
395             break;
396
397         case 'X':
398             if (val)
399                 global.force_width = atoi(val);
400             res = 1;
401             break;
402
403         case 'Y':
404             if (val)
405                 global.force_height = atoi(val);
406             res = 1;
407             break;
408
409         case 'V':
410             printf("png2swf - part of %s %s\n", PACKAGE, VERSION);
411             exit(0);
412
413         default:
414             res = -1;
415             break;
416         }
417
418     if (res < 0) {
419         if (VERBOSE(1))
420             fprintf(stderr, "Unknown option: -%s\n", arg);
421         exit(1);
422         return 0;
423     }
424     return res;
425 }
426
427 struct options_t options[] = 
428
429 {"o", "output"},
430 {"r", "rate"},
431 {"v", "verbose"},
432 {"X", "width"},
433 {"Y", "height"},
434 {"V", "version"},
435 {"s", "scale"}
436 };
437
438 int args_callback_longoption(char *name, char *val)
439 {
440     return args_long2shortoption(options, name, val);
441 }
442
443 int args_callback_command(char *arg, char *next)        // actually used as filename
444 {
445     char *s;
446     int scale;
447     if (CheckInputFile(arg, &s) < 0) {
448         if (VERBOSE(1))
449             fprintf(stderr, "Error opening input file: %s\n", arg);
450         free(s);
451     } else {
452         image[global.nfiles].filename = s;
453         image[global.nfiles].scale = global.prescale;
454         global.nfiles++;
455         if (global.nfiles >= MAX_INPUT_FILES) {
456             if (VERBOSE(1))
457                 fprintf(stderr, "Error: Too many input files.\n");
458             exit(1);
459         }
460     }
461     return 0;
462 }
463
464 void args_callback_usage(char *name)
465 {
466     printf("Usage: %s  [-options [value]] imagefiles[.png] [...]\n", name);
467     printf("-r framerate          (rate) Set movie framerate (100/sec)\n");
468     printf("-o outputfile         (output) Set name for SWF output file\n");
469     printf("-X pixel              (width) Force movie width to scale (default: autodetect)\n");
470     printf("-Y pixel              (height) Force movie height to scale (default: autodetect)\n");
471     printf("-v level              (verbose) Set verbose level (0=quiet, 1=default, 2=debug)\n");
472     printf("-V                    (version) Print version information and exit\n");
473     printf("The following options can be set independently for each image: -q -s\n");
474 }
475
476
477 int main(int argc, char **argv)
478 {
479     SWF swf;
480     TAG *t;
481
482     memset(&global, 0x00, sizeof(global));
483
484     global.framerate = 100;
485     global.verbose = 1;
486     global.prescale = 1;
487
488     processargs(argc, argv);
489
490     if (VERBOSE(2))
491         fprintf(stderr, "Processing %i file(s)...\n", global.nfiles);
492
493     t = MovieStart(&swf, global.framerate,
494                    global.force_width ? global.force_width : global.
495                    max_image_width,
496                    global.force_height ? global.force_height : global.
497                    max_image_height);
498
499     {
500         int i;
501         for (i = 0; i < global.nfiles; i++) {
502             if (VERBOSE(3))
503                 fprintf(stderr, "[%03i] %s (1/%i)\n", i,
504                         image[i].filename, 
505                         image[i].scale);
506             t = MovieAddFrame(&swf, t, image[i].filename, 
507                               image[i].scale, (i * 2) + 1);
508             free(image[i].filename);
509         }
510     }
511
512     MovieFinish(&swf, t, global.outfile);
513
514     return 0;
515 }