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