added -b, -B flags.
[swftools.git] / src / jpeg2swf.c
blob8de7c1ad4d6e846bb77e049cc7025cf745668bdf
1 /* jpeg2swf.c
3 JPEG to SWF converter tool
5 Part of the swftools package.
7 Copyright (c) 2001 Rainer Böhme <rfxswf@reflex-studio.de>
8 Copyright (c) 2002,2003 Matthias Kramm <kramm@quiss.org>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24 #include <stdio.h>
25 #include <math.h>
26 #include <fcntl.h>
27 #include <jpeglib.h>
28 #include "../lib/rfxswf.h"
29 #include "../lib/args.h" // not really a header ;-)
31 #define MAX_INPUT_FILES 1024
32 #define VERBOSE(x) (global.verbose>=x)
34 struct {
35 int quality;
36 float framerate;
37 int max_image_width;
38 int max_image_height;
39 int force_width;
40 int force_height;
41 int nfiles;
42 int verbose;
43 char *outfile;
44 int mx;
45 int version;
46 } global;
48 typedef struct _image {
49 char *filename;
50 int quality;
51 int width;
52 int height;
53 } image_t;
54 image_t image[MAX_INPUT_FILES];
56 VIDEOSTREAM stream;
58 TAG *MovieStart(SWF * swf, float framerate, int dx, int dy)
60 TAG *t;
61 RGBA rgb;
63 memset(swf, 0x00, sizeof(SWF));
65 swf->fileVersion = global.version;
66 swf->frameRate = (int)(256.0 * framerate);
67 swf->movieSize.xmax = dx * 20;
68 swf->movieSize.ymax = dy * 20;
70 t = swf->firstTag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
72 rgb.r = rgb.g = rgb.b = rgb.a = 0x00;
73 swf_SetRGB(t, &rgb);
75 if (global.mx) {
76 t = swf_InsertTag(t, ST_DEFINEVIDEOSTREAM);
77 swf_SetU16(t, 0xf00d);
78 swf_SetVideoStreamDefine(t, &stream, 65535, dx, dy);
81 return t;
84 int MovieFinish(SWF * swf, TAG * t, char *sname)
86 int handle, so = fileno(stdout);
87 t = swf_InsertTag(t, ST_END);
89 if ((!isatty(so)) && (!sname))
90 handle = so;
91 else {
92 if (!sname)
93 sname = "output.swf";
94 handle = open(sname, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, 0666);
96 if (swf_WriteSWF(handle, swf)<0)
97 fprintf(stderr, "Unable to write output file: %s\n", sname);
98 if (handle != so)
99 close(handle);
101 swf_FreeTags(swf);
102 return 0;
105 int getJPEG(char*filename, int* width, int* height, RGBA**pic2)
107 struct jpeg_decompress_struct cinfo;
108 struct jpeg_error_mgr jerr;
109 struct jpeg_source_mgr mgr;
110 int x,y;
111 FILE*f;
112 RGBA*pic,*js;
113 U8*buf;
115 if ((f=fopen(filename,"rb"))==NULL) {
116 fprintf(stderr, "rfxswf: file open error\n");
117 return 0;
120 cinfo.err = jpeg_std_error(&jerr);
121 jpeg_create_decompress(&cinfo);
122 jpeg_stdio_src(&cinfo, f);
123 jpeg_read_header(&cinfo, TRUE);
124 jpeg_start_decompress(&cinfo);
126 pic = malloc(cinfo.output_width*cinfo.output_height*sizeof(RGBA));
127 buf = malloc(cinfo.output_width*4);
128 memset(pic, 255, cinfo.output_width*cinfo.output_height*sizeof(RGBA));
129 js = pic;
131 *width = cinfo.output_width;
132 *height = cinfo.output_height;
134 for (y=0;y<cinfo.output_height;y++) {
135 int x;
136 jpeg_read_scanlines(&cinfo,&buf,1);
138 if(cinfo.out_color_space == JCS_GRAYSCALE) {
139 for(x=0;x<cinfo.output_width;x++) {
140 js[x].r = js[x].g = js[x].b = buf[x];
142 } else if(cinfo.out_color_space == JCS_RGB) {
143 for (x=0;x<cinfo.output_width;x++)
145 js[x].r = buf[x*3+0];
146 js[x].g = buf[x*3+1];
147 js[x].b = buf[x*3+2];
149 } else if(cinfo.out_color_space == JCS_YCCK) {
150 //FIXME
151 fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
152 return -1;
153 } else if(cinfo.out_color_space == JCS_YCbCr) {
154 for(x=0;x<cinfo.output_width;x++) {
155 int y = buf[x*3+0];
156 int u = buf[x*3+1];
157 int v = buf[x*3+1];
158 js[x].r = y + ((360*(v-128))>>8);
159 js[x].g = y - ((88*(u-128)+183*(v-128))>>8);
160 js[x].b = y + ((455 * (u-128))>>8);
163 else if(cinfo.out_color_space == JCS_CMYK)
165 for(x=0;x<cinfo.output_width;x++) {
166 int white = 255 - buf[x*4+3];
167 js[x].r = white - ((buf[x*4]*white)>>8);
168 js[x].g = white - ((buf[x*4+1]*white)>>8);
169 js[x].b = white - ((buf[x*4+2]*white)>>8);
172 js += cinfo.output_width;
175 jpeg_finish_decompress(&cinfo);
176 jpeg_destroy_decompress(&cinfo);
178 free(buf);
179 *pic2 = pic;
180 return 1;
184 int frame = 0;
185 TAG *MovieAddFrame(SWF * swf, TAG * t, char *sname, int quality,
186 int id, int width, int height)
188 SHAPE *s;
189 SRECT r;
190 MATRIX m;
191 int fs;
194 if(global.mx) {
195 int sizex, sizey;
196 RGBA * pic2;
197 SWFPLACEOBJECT obj;
198 int quant=0;
199 getJPEG(sname, &sizex, &sizey, &pic2);
200 if(sizex != stream.owidth || sizey != stream.oheight) {
201 fprintf(stderr, "All images must have the same dimensions if using -m!");
202 exit(1);
205 t = swf_InsertTag(t, ST_VIDEOFRAME);
206 swf_SetU16(t, 0xf00d);
207 quant = 1+(30-(30*quality)/100);
208 if(!(frame&127)) {
209 swf_SetVideoStreamIFrame(t, &stream, pic2, quant);
210 } else {
211 swf_SetVideoStreamPFrame(t, &stream, pic2, quant);
214 t = swf_InsertTag(t, ST_PLACEOBJECT2);
215 swf_GetPlaceObject(0, &obj);
216 if(frame==0) {
217 obj.depth = 1;
218 obj.id = 0xf00d;
219 } else {
220 obj.depth = 1;
221 obj.move = 1;
222 obj.ratio = frame;
224 swf_SetPlaceObject(t,&obj);
226 t = swf_InsertTag(t, ST_SHOWFRAME);
227 } else {
228 t = swf_InsertTag(t, ST_DEFINEBITSJPEG2);
229 swf_SetU16(t, id); // id
230 swf_SetJPEGBits(t,sname,quality);
232 t = swf_InsertTag(t, ST_DEFINESHAPE);
233 swf_ShapeNew(&s);
234 swf_GetMatrix(NULL, &m);
235 m.sx = 20 * 0x10000;
236 m.sy = 20 * 0x10000;
237 fs = swf_ShapeAddBitmapFillStyle(s, &m, id, 0);
238 swf_SetU16(t, id + 1); // id
239 r.xmin = r.ymin = 0;
240 r.xmax = width * 20;
241 r.ymax = height * 20;
242 swf_SetRect(t, &r);
243 swf_SetShapeHeader(t, s);
244 swf_ShapeSetAll(t, s, 0, 0, 0, fs, 0);
245 swf_ShapeSetLine(t, s, r.xmax, 0);
246 swf_ShapeSetLine(t, s, 0, r.ymax);
247 swf_ShapeSetLine(t, s, -r.xmax, 0);
248 swf_ShapeSetLine(t, s, 0, -r.ymax);
249 swf_ShapeSetEnd(t);
251 t = swf_InsertTag(t, ST_REMOVEOBJECT2);
252 swf_SetU16(t, 1); // depth
254 t = swf_InsertTag(t, ST_PLACEOBJECT2);
255 swf_GetMatrix(NULL, &m);
256 m.tx = (swf->movieSize.xmax - (int) width * 20) / 2;
257 m.ty = (swf->movieSize.ymax - (int) height * 20) / 2;
258 swf_ObjectPlace(t, id + 1, 1, &m, NULL, NULL);
260 t = swf_InsertTag(t, ST_SHOWFRAME);
262 frame++;
264 return t;
267 int CheckInputFile(image_t* i, char *fname, char **realname)
269 struct jpeg_decompress_struct cinfo;
270 struct jpeg_error_mgr jerr;
271 FILE *f;
272 char *s = malloc(strlen(fname) + 5);
273 int width, height;
275 if (!s)
276 exit(2);
277 (*realname) = s;
278 strcpy(s, fname);
280 // Check whether file exists (with typical extensions)
282 if ((f = fopen(s, "rb")) == NULL) {
283 sprintf(s, "%s.jpg", fname);
284 if ((f = fopen(s, "rb")) == NULL) {
285 sprintf(s, "%s.jpeg", fname);
286 if ((f = fopen(s, "rb")) == NULL) {
287 sprintf(s, "%s.JPG", fname);
288 if ((f = fopen(s, "rb")) == NULL) {
289 sprintf(s, "%s.JPEG", fname);
290 if ((f = fopen(s, "rb")) == NULL)
291 return -1;
297 cinfo.err = jpeg_std_error(&jerr);
298 jpeg_create_decompress(&cinfo);
299 jpeg_stdio_src(&cinfo, f);
300 jpeg_read_header(&cinfo, TRUE);
302 width = cinfo.image_width;
303 height = cinfo.image_height;
305 i->width = width;
306 i->height = height;
308 // Get image dimensions
310 if (global.max_image_width < width)
311 global.max_image_width = width;
312 if (global.max_image_height < height)
313 global.max_image_height = height;
315 jpeg_destroy_decompress(&cinfo);
316 fclose(f);
318 return 0;
321 int args_callback_option(char *arg, char *val)
323 int res = 0;
324 if (arg[1])
325 res = -1;
326 else
327 switch (arg[0]) {
328 case 'q':
329 if (val)
330 global.quality = atoi(val);
331 if ((global.quality < 1) ||(global.quality > 100)) {
332 if (VERBOSE(1))
333 fprintf(stderr,
334 "Error: You must specify a valid quality between 1 and 100.\n");
335 exit(1);
337 res = 1;
338 break;
340 case 'r':
341 if (val)
342 global.framerate = atof(val);
343 if ((global.framerate < 1.0/256) || (global.framerate >= 256.0)) {
344 if (VERBOSE(1))
345 fprintf(stderr,
346 "Error: You must specify a valid framerate between 1 and 10000.\n");
347 exit(1);
349 res = 1;
350 break;
352 case 'o':
353 if (val)
354 global.outfile = val;
355 res = 1;
356 break;
358 case 'v':
359 if (val)
360 global.verbose = atoi(val);
361 res = 1;
362 break;
364 case 'X':
365 if (val)
366 global.force_width = atoi(val);
367 res = 1;
368 break;
370 case 'm':
371 global.mx = 1;
372 global.version = 6;
373 return 0;
375 case 'Y':
376 if (val)
377 global.force_height = atoi(val);
378 res = 1;
379 break;
381 case 'V':
382 printf("jpeg2swf - part of %s %s\n", PACKAGE, VERSION);
383 exit(0);
385 default:
386 res = -1;
387 break;
390 if (res < 0) {
391 if (VERBOSE(1))
392 fprintf(stderr, "Unknown option: -%s\n", arg);
393 exit(1);
394 return 0;
396 return res;
399 static struct options_t options[] = {
400 {"o", "output"},
401 {"m", "mx"},
402 {"q", "quality"},
403 {"r", "rate"},
404 {"X", "width"},
405 {"Y", "height"},
406 {"v", "verbose"},
407 {"V", "version"},
408 {0,0}
411 int args_callback_longoption(char *name, char *val)
413 return args_long2shortoption(options, name, val);
416 int args_callback_command(char *arg, char *next) // actually used as filename
418 char *s;
419 image_t* i = &image[global.nfiles];
420 if (CheckInputFile(i, arg, &s) < 0) {
421 if (VERBOSE(1))
422 fprintf(stderr, "Unable to open input file: %s\n", arg);
423 free(s);
424 } else {
425 i->filename = s;
426 i->quality = global.quality;
427 global.nfiles++;
428 if (global.nfiles >= MAX_INPUT_FILES) {
429 if (VERBOSE(1))
430 fprintf(stderr, "Error: Too many input files.\n");
431 exit(1);
434 return 0;
437 void args_callback_usage(char *name)
439 printf("\n");
440 printf("Usage: %s [-options [value]] imagefiles[.jpg]|[.jpeg] [...]\n", name);
441 printf("\n");
442 printf("-o , --output <outputfile> Explicitly specify output file. (otherwise, output.swf will be used)\n");
443 printf("-m , --mx Use Flash MX H.263 compression (use for correlated images)\n");
444 printf("-q , --quality <quality> Set compression quality (1-100, 1=worst, 100=best)\n");
445 printf("-r , --rate <framerate> Set movie framerate (frames per second)\n");
446 printf("-X , --width <width> Force movie width to <width> (default: autodetect)\n");
447 printf("-Y , --height <height> Force movie height to <height> (default: autodetect)\n");
448 printf("-v , --verbose <level> Set verbose level to <level> (0=quiet, 1=default, 2=debug)\n");
449 printf("-V , --version Print version information and exit\n");
450 printf("\n");
454 int main(int argc, char **argv)
456 SWF swf;
457 TAG *t;
459 memset(&global, 0x00, sizeof(global));
461 global.quality = 60;
462 global.framerate = 1.0;
463 global.verbose = 1;
464 global.version = 4;
466 processargs(argc, argv);
468 if (VERBOSE(2))
469 fprintf(stderr, "Processing %i file(s)...\n", global.nfiles);
471 t = MovieStart(&swf, global.framerate,
472 global.force_width ? global.force_width : global.
473 max_image_width,
474 global.force_height ? global.force_height : global.
475 max_image_height);
478 int i;
479 for (i = 0; i < global.nfiles; i++) {
480 if (VERBOSE(3))
481 fprintf(stderr, "[%03i] %s (%i%%, 1/%i)\n", i,
482 image[i].filename, image[i].quality);
483 t = MovieAddFrame(&swf, t, image[i].filename, image[i].quality,
484 (i * 2) + 1,
485 image[i].width, image[i].height);
486 free(image[i].filename);
490 MovieFinish(&swf, t, global.outfile);
492 return 0;