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