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