initial revision
[swftools.git] / src / jpeg2swf.c
1 /* jpeg2swf.c
2
3    JPEG to SWF converter tool
4
5    Part of the swftools package.
6
7    Copyright (c) 2001 Rainer Böhme <rfxswf@reflex-studio.de>
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 <jpeglib.h>
17 #include "../lib/rfxswf.h"
18 #include "../lib/args.h" // not really a header ;-)
19
20 #define MAX_INPUT_FILES 1024
21 #define VERBOSE(x) (global.verbose>=x)
22
23 struct
24 { int quality;
25   int framerate;
26   int max_image_width;
27   int max_image_height;
28   int force_width;
29   int force_height;
30   int prescale;
31   int nfiles;
32   int verbose;
33   char * outfile;
34 } global;
35
36 struct
37 { char * filename;
38   int scale;
39   int quality;
40 } image[MAX_INPUT_FILES];
41
42 TAG * MovieStart(SWF * swf,int framerate,int dx,int dy)
43 { TAG * t;
44   RGBA rgb;
45
46   memset(swf,0x00,sizeof(SWF));
47
48   swf->fileVersion       = 4;
49   swf->frameRate         = (25600/framerate);
50   swf->movieSize.xmax    = dx*20;
51   swf->movieSize.ymax    = dy*20;
52
53   t = swf->firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
54
55   rgb.r = rgb.g = rgb.b = rgb.a  = 0x00;
56   swf_SetRGB(t,&rgb);
57
58   return t;
59 }
60
61 int MovieFinish(SWF * swf,TAG * t,char * sname)
62 {  int handle, so = fileno(stdout);
63    t = swf_InsertTag(t,ST_END);
64
65    if ((!isatty(so))&&(!sname)) handle = so;
66    else
67    { if (!sname) sname = "output.swf";
68      handle = open(sname,O_BINARY|O_RDWR|O_CREAT|O_TRUNC,0666);
69    }
70    if FAILED(swf_WriteSWF(handle,swf)) if (VERBOSE(1)) fprintf(stderr,"Unable to write output file: %s\n",sname);
71    if (handle!=so) close(handle);
72    
73    swf_FreeTags(swf);
74    return 0;
75 }
76
77 TAG * MovieAddFrame(SWF * swf,TAG * t,char * sname,int quality,int scale,int id)
78 { SHAPE * s;
79   SRECT r;
80   MATRIX m;
81   int fs;
82   
83   struct jpeg_decompress_struct cinfo;
84   struct jpeg_error_mgr jerr;
85   LPJPEGBITS out;
86   FILE * f;
87   U8 * scanline;
88
89   if ((f=fopen(sname,"rb"))==NULL)
90   { if (VERBOSE(1)) fprintf(stderr,"Read access failed: %s\n",sname);
91     return t;
92   }
93   
94   cinfo.err = jpeg_std_error(&jerr);
95   jpeg_create_decompress(&cinfo); 
96   jpeg_stdio_src(&cinfo,f);
97   jpeg_read_header(&cinfo, TRUE);
98   
99   if (scale>1)
100   { cinfo.scale_num = 1;
101     cinfo.scale_denom = scale;
102   }
103     
104   jpeg_start_decompress(&cinfo);
105
106   t = swf_InsertTag(t,ST_DEFINEBITSJPEG2);
107
108         swf_SetU16(t,id);  // id
109   
110         out = swf_SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
111         scanline = (U8*)malloc(4*cinfo.output_width);
112  
113         // the following code is a duplication of swf_SetJPEGBits in ../lib/modules/swfbits.c
114         if (scanline)
115         { int y;
116           U8 * js = scanline;
117
118           if(cinfo.out_color_space == JCS_GRAYSCALE) 
119           {
120               for (y=0;y<cinfo.output_height;y++)
121               { int x;
122                 jpeg_read_scanlines(&cinfo,&js,1);
123                 for(x=cinfo.output_width-1;x>=0;x--) {
124                     js[x*3] = js[x*3+1] = js[x*3+2] = js[x];
125                 }
126                 swf_SetJPEGBitsLines(out,(U8**)&js,1);
127               }
128           }
129           else if(cinfo.out_color_space == JCS_RGB) 
130           {
131               for (y=0;y<cinfo.output_height;y++)
132               { jpeg_read_scanlines(&cinfo,&js,1);
133                 swf_SetJPEGBitsLines(out,(U8**)&js,1);
134               }
135           }
136           free(scanline);
137         }
138         
139         swf_SetJPEGBitsFinish(out);
140     
141   t = swf_InsertTag(t,ST_DEFINESHAPE);
142
143         swf_ShapeNew(&s);
144         swf_GetMatrix(NULL,&m);
145         m.sx = 20*0x10000;
146         m.sy = 20*0x10000;
147         fs = swf_ShapeAddBitmapFillStyle(s,&m,id,0);
148         
149         swf_SetU16(t,id+1); // id
150
151
152         r.xmin = r.ymin = 0;
153         r.xmax = cinfo.output_width*20;
154         r.ymax = cinfo.output_height*20;
155         swf_SetRect(t,&r);
156         
157         swf_SetShapeHeader(t,s);
158
159         swf_ShapeSetAll(t,s,0,0,0,fs,0);
160         swf_ShapeSetLine(t,s,r.xmax,0);
161         swf_ShapeSetLine(t,s,0,r.ymax);
162         swf_ShapeSetLine(t,s,-r.xmax,0);
163         swf_ShapeSetLine(t,s,0,-r.ymax);
164         
165         swf_ShapeSetEnd(t);
166         
167   t = swf_InsertTag(t,ST_REMOVEOBJECT2);
168         swf_SetU16(t,1); // depth
169
170   t = swf_InsertTag(t,ST_PLACEOBJECT2);
171
172         swf_GetMatrix(NULL,&m);
173         m.tx = (swf->movieSize.xmax-(int)cinfo.output_width*20)/2;
174         m.ty = (swf->movieSize.ymax-(int)cinfo.output_height*20)/2;
175         swf_ObjectPlace(t,id+1,1,&m,NULL,NULL);
176
177   t = swf_InsertTag(t,ST_SHOWFRAME);
178
179   jpeg_finish_decompress(&cinfo);
180   fclose(f);
181
182   return t;
183 }
184
185 int CheckInputFile(char * fname,char ** realname)
186 { struct jpeg_decompress_struct cinfo;
187   struct jpeg_error_mgr jerr;
188   FILE * f;
189   char * s = malloc(strlen(fname)+5);
190   int width, height;
191   
192   if (!s) exit(2);
193   (*realname) = s;
194   strcpy(s,fname);
195
196   // Check whether file exists (with typical extensions)
197
198   if ((f=fopen(s,"rb"))==NULL)
199   { sprintf(s,"%s.jpg",fname);
200     if ((f=fopen(s,"rb"))==NULL)
201     { sprintf(s,"%s.jpeg",fname);
202       if ((f=fopen(s,"rb"))==NULL)
203       { sprintf(s,"%s.JPG",fname);
204         if ((f=fopen(s,"rb"))==NULL)
205         { sprintf(s,"%s.JPEG",fname);
206           if ((f=fopen(s,"rb"))==NULL)
207             return -1;
208         }
209       }
210     }
211   }
212   
213   cinfo.err = jpeg_std_error(&jerr);
214   jpeg_create_decompress(&cinfo); 
215   jpeg_stdio_src(&cinfo,f);
216   jpeg_read_header(&cinfo, TRUE);
217
218   // Apply scaling (scale option can be used several times to set different scales)
219
220   if (global.prescale>1)
221   { cinfo.scale_num = 1;
222     cinfo.scale_denom = global.prescale;
223
224     jpeg_calc_output_dimensions(&cinfo);
225
226     width  = cinfo.output_width;
227     height = cinfo.output_height;
228   }
229   else
230   { width  = cinfo.image_width;
231     height = cinfo.image_height;
232   }
233
234   // Get image dimensions
235
236   if (global.max_image_width<width) global.max_image_width = width;
237   if (global.max_image_height<height) global.max_image_height = height;
238   
239   jpeg_destroy_decompress(&cinfo);
240   fclose(f);
241
242   return 0;
243 }
244
245 int args_callback_option(char*arg,char*val)
246 { int res = 0;
247   if (arg[1]) res = -1;
248   else switch (arg[0])
249   { case 'q':
250       if (val) global.quality = atoi(val);
251       if ((global.quality<1)||(global.quality>100))
252       { if (VERBOSE(1)) fprintf(stderr,"Error: You must specify a valid quality between 1 and 100.\n");
253         exit(1);
254       }
255       res = 1;
256       break;
257
258     case 'r':
259       if (val) global.framerate = atoi(val);
260       if ((global.framerate<1)||(global.framerate>5000))
261       { if (VERBOSE(1)) fprintf(stderr,"Error: You must specify a valid framerate between 1 and 10000.\n");
262         exit(1);
263       }
264       res = 1;
265       break;
266
267     case 's':
268       if (val) global.prescale = atoi(val);
269       if (!((global.prescale==1)||(global.prescale==2)||(global.prescale==4)||(global.prescale==8)))
270       { if (VERBOSE(1)) fprintf(stderr,"Error: Prescale denominator is limited to 2, 4 or 8\n");
271         exit(1);
272       }
273       res = 1;
274       break;
275
276     case 'o':
277       if (val) global.outfile = val; res = 1; break;
278
279     case 'v':
280       if (val) global.verbose = atoi(val); res = 1; break;
281
282     case 'X':
283       if (val) global.force_width = atoi(val); res = 1; break;
284
285     case 'Y':
286       if (val) global.force_height = atoi(val); res = 1; break;
287
288     case 'V':
289       printf("jpeg2swf - part of %s %s\n", PACKAGE, VERSION);exit(0);
290       
291     default:
292       res = -1;
293       break;
294   }
295   
296   if (res<0)
297   { if (VERBOSE(1)) fprintf(stderr,"Unknown option: -%s\n",arg);
298     exit(1);
299     return 0;
300   }
301   return res;
302 }
303
304 struct options_t options[] =
305 {{"q","quality"},
306  {"o","output"},
307  {"r","rate"},
308  {"v","verbose"},
309  {"X","width"},
310  {"Y","height"},
311  {"V","version"},
312  {"s","scale"}
313  };
314
315 int args_callback_longoption(char*name,char*val) {
316     return args_long2shortoption(options, name, val);
317 }
318
319 int args_callback_command(char*arg,char*next)  // actually used as filename
320 { char * s;
321   int scale;
322   if (CheckInputFile(arg,&s)<0)
323   { if (VERBOSE(1)) fprintf(stderr, "Unable to open input file: %s\n",arg);
324     free(s);
325   }
326   else
327   { image[global.nfiles].filename = s;
328     image[global.nfiles].scale   = global.prescale;
329     image[global.nfiles].quality = global.quality;
330     global.nfiles++;
331     if (global.nfiles>=MAX_INPUT_FILES)
332     { if (VERBOSE(1)) fprintf(stderr, "Error: Too many input files.\n");
333       exit(1);
334     }
335   }
336   return 0;
337 }
338
339 void args_callback_usage(char*name)
340 { fprintf(stderr,"Usage: %s  [-options [value]] imagefiles[.jpg]|[.jpeg] [...]\n",name);
341   fprintf(stderr,"-q quality            (quality) Set JPEG compression quality (1-100)\n");
342   fprintf(stderr,"-s denominator        (scale) 2, 4 or 8: Reduce image size to 1/2, 1/4, 1/8\n");
343   fprintf(stderr,"-r framerate          (rate) Set movie framerate (100/sec)\n");
344   fprintf(stderr,"-o outputfile         (output) Set name for SWF output file\n");
345   fprintf(stderr,"-X pixel              (width) Force movie width to scale (default: autodetect)\n");
346   fprintf(stderr,"-Y pixel              (height) Force movie height to scale (default: autodetect)\n");
347   fprintf(stderr,"-v level              (verbose) Set verbose level (0=quiet, 1=default, 2=debug)\n");
348   fprintf(stderr,"-V                    (version) Print version information and exit\n");
349   fprintf(stderr,"The following options can be set independently for each image: -q -s\n");
350 }
351
352
353 int main(int argc, char ** argv)
354 { SWF swf;
355   TAG * t;
356
357   memset(&global,0x00,sizeof(global));
358     
359   global.quality                = 60;
360   global.framerate              = 100;
361   global.verbose                = 1;
362   global.prescale               = 1;
363   
364   processargs(argc, argv);
365
366   if (VERBOSE(2)) fprintf(stderr,"Processing %i file(s)...\n",global.nfiles);
367
368   t = MovieStart(&swf,global.framerate,
369                       global.force_width?global.force_width:global.max_image_width,
370                       global.force_height?global.force_height:global.max_image_height);
371
372   { int i;
373     for (i=0;i<global.nfiles;i++)
374     { if (VERBOSE(3)) fprintf(stderr,"[%03i] %s (%i%%, 1/%i)\n",i,image[i].filename,image[i].quality,image[i].scale);
375       t = MovieAddFrame(&swf,t,image[i].filename,
376                                image[i].quality,
377                                image[i].scale,(i*2)+1);
378       free(image[i].filename);
379     }
380   }
381
382   MovieFinish(&swf,t,global.outfile);
383
384   return 0;
385 }
386
387
388 // Old main routine
389
390 /*
391 int ConvertJPEG2SWF(char * sname,char * dname,int quality)
392 { RGBA rgb;
393   SWF swf;
394   TAG * t;
395   
396   SHAPE * s;
397   SRECT r;
398   MATRIX m;
399   int fs;
400   
401   struct jpeg_decompress_struct cinfo;
402   struct jpeg_error_mgr jerr;
403   LPJPEGBITS out;
404   FILE * f;
405   U8 * scanline;
406
407   int handle;
408
409   cinfo.err = jpeg_std_error(&jerr);
410   jpeg_create_decompress(&cinfo); 
411
412   if ((f=fopen(sname,"rb"))==NULL)
413   { fprintf(stderr,"Read access failed: %s\n",sname);
414     return -1;
415   }
416   
417   jpeg_stdio_src(&cinfo,f);
418   jpeg_read_header(&cinfo, TRUE);
419   jpeg_start_decompress(&cinfo);
420   
421   memset(&swf,0x00,sizeof(SWF));
422
423   swf.FileVersion       = 4;
424   swf.FrameRate         = 0x1000;
425   swf.MovieSize.xmax    = cinfo.output_width*20;
426   swf.MovieSize.ymax    = cinfo.output_height*20;
427
428   printf("dx = %i, dy = %i\n",cinfo.output_width,cinfo.output_height);
429
430   t = swf.FirstTag = InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
431
432         rgb.r = rgb.g = rgb.b = rgb.a  = 0x00;
433         SetRGB(t,&rgb);
434
435   t = InsertTag(t,ST_DEFINEBITSJPEG2);
436
437         SetU16(t,1);  // id
438   
439         out = SetJPEGBitsStart(t,cinfo.output_width,cinfo.output_height,quality);
440         scanline = (U8*)malloc(4*cinfo.output_width);
441   
442         if (scanline)
443         { int y;
444           U8 * js = scanline;
445           for (y=0;y<cinfo.output_height;y++)
446           { jpeg_read_scanlines(&cinfo,&js,1);
447             SetJPEGBitsLines(out,(U8**)&js,1);
448           }
449           free(scanline);
450         }
451         
452         SetJPEGBitsFinish(out);
453
454         printf("JPEG Tag-Length: %06x\n",GetDataSize(t));
455
456   t = InsertTag(t,ST_DEFINESHAPE);
457
458         NewShape(&s);
459         GetMatrix(NULL,&m);
460         m.sx = 20*0x10000;
461         m.sy = 20*0x10000;
462         rgb.r = 0xff;
463         fs = ShapeAddBitmapFillStyle(s,&m,1,0);
464 //        fs = ShapeAddSolidFillStyle(s,&rgb);
465         
466         SetU16(t,2); // id
467         SetRect(t,&swf.MovieSize);
468         SetShapeHeader(t,s);
469
470         ShapeSetAll(t,s,0,0,0,fs,0);
471         ShapeSetLine(t,s,swf.MovieSize.xmax,0);
472         ShapeSetLine(t,s,0,swf.MovieSize.ymax);
473         ShapeSetLine(t,s,-swf.MovieSize.xmax,0);
474         ShapeSetLine(t,s,0,-swf.MovieSize.ymax);
475         
476         ShapeSetEnd(t);
477
478   t = InsertTag(t,ST_PLACEOBJECT2);
479
480         ObjectPlace(t,2,1,NULL,NULL,NULL);
481
482   t = InsertTag(t,ST_SHOWFRAME);
483   
484   t = InsertTag(t,ST_END);
485
486   jpeg_finish_decompress(&cinfo);
487   fclose(f);
488   
489   handle = open(dname,O_BINARY|O_RDWR|O_CREAT|O_TRUNC,0666);
490   if FAILED(WriteSWF(handle,&swf)) fprintf(stderr,"WriteSWF() failed.\n");
491   close(handle);
492
493   return 0;
494 }
495 */
496