fixed buffer overflow
[swftools.git] / src / jpeg2swf.c
blob4417450047e0323558c7234222afc159809f1526
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 static 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 next_id;
46 char *asset_name;
47 int version;
48 int fit_to_movie;
49 float scale;
50 } global;
52 static int custom_move=0;
53 static int move_x=0;
54 static int move_y=0;
55 static int clip_x1=0,clip_y1=0,clip_x2=0,clip_y2=0;
56 static int custom_clip = 0;
58 typedef struct _image {
59 char *filename;
60 int quality;
61 int width;
62 int height;
63 } image_t;
64 image_t image[MAX_INPUT_FILES];
66 VIDEOSTREAM stream;
68 TAG *MovieStart(SWF * swf, float framerate, int dx, int dy)
70 TAG *t;
71 RGBA rgb;
73 memset(swf, 0x00, sizeof(SWF));
75 swf->fileVersion = global.version;
76 swf->frameRate = (int)(256.0 * framerate);
78 if(custom_clip) {
79 swf->movieSize.xmin = clip_x1 * 20;
80 swf->movieSize.ymin = clip_y1 * 20;
81 swf->movieSize.xmax = clip_x2 * 20;
82 swf->movieSize.ymax = clip_y2 * 20;
83 } else {
84 swf->movieSize.xmin = 0;
85 swf->movieSize.ymin = 0;
86 swf->movieSize.xmax = swf->movieSize.xmin + dx * 20;
87 swf->movieSize.ymax = swf->movieSize.ymin + dy * 20;
90 t = swf->firstTag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
92 rgb.r = rgb.g = rgb.b = rgb.a = 0x00;
93 swf_SetRGB(t, &rgb);
95 if (global.mx) {
96 t = swf_InsertTag(t, ST_DEFINEVIDEOSTREAM);
97 swf_SetU16(t, 0xf00d);
98 swf_SetVideoStreamDefine(t, &stream, 65535, dx, dy);
99 } else if (global.asset_name) {
100 t = swf_InsertTag(t, ST_DEFINESPRITE);
101 swf_SetU16(t, 1);
102 swf_SetU16(t, global.next_id++);
105 return t;
108 int MovieFinish(SWF * swf, TAG * t, char *sname)
110 int handle, so = fileno(stdout);
112 if (global.asset_name) {
113 SWFPLACEOBJECT obj;
115 t = swf_InsertTag(t, ST_END);
116 t = swf_InsertTag(t, ST_EXPORTASSETS);
117 swf_SetU16(t, 1);
118 swf_SetU16(t, 1);
119 swf_SetString(t, global.asset_name);
121 t = swf_InsertTag(t, ST_PLACEOBJECT2);
122 swf_GetPlaceObject(0, &obj);
123 obj.depth = 1;
124 obj.id = 1;
125 swf_SetPlaceObject(t, &obj);
127 t = swf_InsertTag(t, ST_SHOWFRAME);
130 t = swf_InsertTag(t, ST_END);
132 if ((!isatty(so)) && (!sname))
133 handle = so;
134 else {
135 if (!sname)
136 sname = "output.swf";
137 handle = open(sname, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, 0666);
139 if(handle<0 && sname) {
140 perror(sname);
142 if (swf_WriteSWF(handle, swf)<0)
143 fprintf(stderr, "Unable to write output file: %s\n", sname);
145 if (handle != so)
146 close(handle);
148 swf_FreeTags(swf);
149 return 0;
152 int getJPEG(char*filename, int* width, int* height, RGBA**pic2)
154 struct jpeg_decompress_struct cinfo;
155 struct jpeg_error_mgr jerr;
156 struct jpeg_source_mgr mgr;
157 int x,y;
158 FILE*f;
159 RGBA*pic,*js;
160 U8*buf;
162 if ((f=fopen(filename,"rb"))==NULL) {
163 fprintf(stderr, "rfxswf: file open error\n");
164 return 0;
167 cinfo.err = jpeg_std_error(&jerr);
168 jpeg_create_decompress(&cinfo);
169 jpeg_stdio_src(&cinfo, f);
170 jpeg_read_header(&cinfo, TRUE);
171 jpeg_start_decompress(&cinfo);
173 pic = malloc(cinfo.output_width*cinfo.output_height*sizeof(RGBA));
174 buf = malloc(cinfo.output_width*4);
175 memset(pic, 255, cinfo.output_width*cinfo.output_height*sizeof(RGBA));
176 js = pic;
178 *width = cinfo.output_width;
179 *height = cinfo.output_height;
181 for (y=0;y<cinfo.output_height;y++) {
182 int x;
183 jpeg_read_scanlines(&cinfo,&buf,1);
185 if(cinfo.out_color_space == JCS_GRAYSCALE) {
186 for(x=0;x<cinfo.output_width;x++) {
187 js[x].r = js[x].g = js[x].b = buf[x];
189 } else if(cinfo.out_color_space == JCS_RGB) {
190 for (x=0;x<cinfo.output_width;x++)
192 js[x].r = buf[x*3+0];
193 js[x].g = buf[x*3+1];
194 js[x].b = buf[x*3+2];
196 } else if(cinfo.out_color_space == JCS_YCCK) {
197 //FIXME
198 fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
199 return -1;
200 } else if(cinfo.out_color_space == JCS_YCbCr) {
201 for(x=0;x<cinfo.output_width;x++) {
202 int y = buf[x*3+0];
203 int u = buf[x*3+1];
204 int v = buf[x*3+1];
205 js[x].r = y + ((360*(v-128))>>8);
206 js[x].g = y - ((88*(u-128)+183*(v-128))>>8);
207 js[x].b = y + ((455 * (u-128))>>8);
210 else if(cinfo.out_color_space == JCS_CMYK)
212 for(x=0;x<cinfo.output_width;x++) {
213 int white = 255 - buf[x*4+3];
214 js[x].r = white - ((buf[x*4]*white)>>8);
215 js[x].g = white - ((buf[x*4+1]*white)>>8);
216 js[x].b = white - ((buf[x*4+2]*white)>>8);
219 js += cinfo.output_width;
222 jpeg_finish_decompress(&cinfo);
223 jpeg_destroy_decompress(&cinfo);
225 free(buf);
226 *pic2 = pic;
227 return 1;
231 int frame = 0;
232 TAG *MovieAddFrame(SWF * swf, TAG * t, char *sname, int quality,
233 int width, int height)
235 SHAPE *s;
236 SRECT r;
237 MATRIX m;
238 int fs;
239 int movie_width = swf->movieSize.xmax - swf->movieSize.xmin;
240 int movie_height = swf->movieSize.ymax - swf->movieSize.ymin;
242 if(global.mx) {
243 int sizex, sizey;
244 RGBA * pic2;
245 SWFPLACEOBJECT obj;
246 int quant=0;
247 getJPEG(sname, &sizex, &sizey, &pic2);
248 if(sizex != stream.owidth || sizey != stream.oheight) {
249 fprintf(stderr, "All images must have the same dimensions if using -m!");
250 exit(1);
253 t = swf_InsertTag(t, ST_VIDEOFRAME);
254 swf_SetU16(t, 0xf00d);
255 quant = 1+(30-(30*quality)/100);
256 if(!(frame%20)) {
257 swf_SetVideoStreamIFrame(t, &stream, pic2, quant);
258 } else {
259 swf_SetVideoStreamPFrame(t, &stream, pic2, quant);
262 t = swf_InsertTag(t, ST_PLACEOBJECT2);
263 swf_GetPlaceObject(0, &obj);
264 if(frame==0) {
265 obj.depth = 1;
266 obj.id = 0xf00d;
267 } else {
268 obj.depth = 1;
269 obj.move = 1;
270 obj.ratio = frame;
272 swf_SetPlaceObject(t,&obj);
274 t = swf_InsertTag(t, ST_SHOWFRAME);
275 } else {
276 t = swf_InsertTag(t, ST_DEFINEBITSJPEG2);
277 swf_SetU16(t, global.next_id); // id
278 swf_SetJPEGBits(t,sname,quality);
280 t = swf_InsertTag(t, ST_DEFINESHAPE);
281 swf_ShapeNew(&s);
282 swf_GetMatrix(NULL, &m);
283 if (global.fit_to_movie) {
284 m.sx = 0x10000 * movie_width / width;
285 m.sy = 0x10000 * movie_height / height;
286 width = movie_width / 20;
287 height = movie_height / 20;
288 } else {
289 m.sx = 20 * 0x10000;
290 m.sy = 20 * 0x10000;
292 m.tx = 0;
293 m.ty = 0;
294 fs = swf_ShapeAddBitmapFillStyle(s, &m, global.next_id, 1);
295 global.next_id++;
296 swf_SetU16(t, global.next_id); // id
297 r.xmin = 0;
298 r.ymin = 0;
299 r.xmax = r.xmin + width * 20;
300 r.ymax = r.ymin + height * 20;
301 swf_SetRect(t, &r);
302 swf_SetShapeHeader(t, s);
303 swf_ShapeSetAll(t, s, r.xmin, r.ymin, 0, fs, 0);
304 swf_ShapeSetLine(t, s, r.xmax - r.xmin, 0);
305 swf_ShapeSetLine(t, s, 0, r.ymax - r.ymin);
306 swf_ShapeSetLine(t, s, -r.xmax + r.xmin, 0);
307 swf_ShapeSetLine(t, s, 0, -r.ymax + r.ymin);
308 swf_ShapeSetEnd(t);
310 if(frame) {
311 t = swf_InsertTag(t, ST_REMOVEOBJECT2);
312 swf_SetU16(t, 1); // depth
315 t = swf_InsertTag(t, ST_PLACEOBJECT2);
316 swf_GetMatrix(NULL, &m);
317 m.sx = (int)(0x10000 * global.scale);
318 m.sy = (int)(0x10000 * global.scale);
320 if(custom_move) {
321 m.tx = move_x*20;
322 m.ty = move_y*20;
323 } else {
324 m.tx = (movie_width - (width * global.scale * 20)) / 2;
325 m.ty = (movie_height - (height * global.scale * 20)) / 2;
327 swf_ObjectPlace(t, global.next_id, 1, &m, NULL, NULL);
328 global.next_id++;
329 t = swf_InsertTag(t, ST_SHOWFRAME);
331 frame++;
333 return t;
336 int CheckInputFile(image_t* i, char *fname, char **realname)
338 struct jpeg_decompress_struct cinfo;
339 struct jpeg_error_mgr jerr;
340 FILE *f;
341 char *s = malloc(strlen(fname) + 5);
342 int width, height;
344 if (!s)
345 exit(2);
346 (*realname) = s;
347 strcpy(s, fname);
349 // Check whether file exists (with typical extensions)
351 if ((f = fopen(s, "rb")) == NULL) {
352 sprintf(s, "%s.jpg", fname);
353 if ((f = fopen(s, "rb")) == NULL) {
354 sprintf(s, "%s.jpeg", fname);
355 if ((f = fopen(s, "rb")) == NULL) {
356 sprintf(s, "%s.JPG", fname);
357 if ((f = fopen(s, "rb")) == NULL) {
358 sprintf(s, "%s.JPEG", fname);
359 if ((f = fopen(s, "rb")) == NULL)
360 return -1;
366 cinfo.err = jpeg_std_error(&jerr);
367 jpeg_create_decompress(&cinfo);
368 jpeg_stdio_src(&cinfo, f);
369 jpeg_read_header(&cinfo, TRUE);
371 width = cinfo.image_width;
372 height = cinfo.image_height;
374 i->width = width;
375 i->height = height;
377 // Get image dimensions
379 if (global.max_image_width < width)
380 global.max_image_width = width;
381 if (global.max_image_height < height)
382 global.max_image_height = height;
384 jpeg_destroy_decompress(&cinfo);
385 fclose(f);
387 return 0;
390 int args_callback_option(char *arg, char *val)
392 int res = 0;
393 if (arg[1])
394 res = -1;
395 else
396 switch (arg[0]) {
397 case 'q':
398 if (val)
399 global.quality = atoi(val);
400 if ((global.quality < 1) ||(global.quality > 100)) {
401 if (VERBOSE(1))
402 fprintf(stderr,
403 "Error: You must specify a valid quality between 1 and 100.\n");
404 exit(1);
406 res = 1;
407 break;
409 case 'r':
410 if (val)
411 global.framerate = atof(val);
412 if ((global.framerate < 1.0/256) || (global.framerate >= 256.0)) {
413 if (VERBOSE(1))
414 fprintf(stderr,
415 "Error: You must specify a valid framerate between 1 and 10000.\n");
416 exit(1);
418 res = 1;
419 break;
421 case 'o':
422 if (val)
423 global.outfile = val;
424 res = 1;
425 break;
427 case 'v':
428 global.verbose++;
429 res = 0;
430 break;
432 /* case 'q':
433 global.verbose--;
434 if(global.verbose<0)
435 global.verbose = 0;
436 res = 0;
437 break;*/
439 case 'X':
440 if (val)
441 global.force_width = atoi(val);
442 res = 1;
443 break;
445 case 'z':
446 global.version = 6;
447 return 0;
449 case 'Y':
450 if (val)
451 global.force_height = atoi(val);
452 res = 1;
453 break;
455 case 'V':
456 printf("jpeg2swf - part of %s %s\n", PACKAGE, VERSION);
457 exit(0);
459 case 'e':
460 if (val)
461 global.asset_name = val;
462 res = 1;
463 break;
465 case 'T':
466 global.version = atoi(val);
467 res = 1;
468 break;
470 case 'f':
471 global.fit_to_movie = 1;
472 res = 0;
473 break;
475 case 'c': {
476 char*s = strdup(val);
477 char*x1 = strtok(s, ":");
478 char*y1 = strtok(0, ":");
479 char*x2 = strtok(0, ":");
480 char*y2 = strtok(0, ":");
481 if(!(x1 && y1 && x2 && y2)) {
482 fprintf(stderr, "-m option requires four arguments, <x1>:<y1>:<x2>:<y2>\n");
483 exit(1);
485 custom_clip = 1;
486 clip_x1 = atoi(x1);
487 clip_y1 = atoi(y1);
488 clip_x2 = atoi(x2);
489 clip_y2 = atoi(y2);
490 free(s);
492 res = 1;
493 break;
496 case 'M': {
497 global.mx = 1;
498 res = 1;
499 break;
502 case 'm': {
503 char*s = strdup(val);
504 char*c = strchr(s, ':');
505 if(!c) {
506 fprintf(stderr, "-m option requires two arguments, <x>:<y>\n");
507 exit(1);
509 *c = 0;
510 custom_move = 1;
511 move_x = atoi(val);
512 move_y = atoi(c+1);
513 free(s);
515 res = 1;
516 break;
519 case 's': {
520 global.scale = atof(val)/100;
521 res = 1;
522 break;
525 default:
526 res = -1;
527 break;
530 if (res < 0) {
531 if (VERBOSE(1))
532 fprintf(stderr, "Unknown option: -%s\n", arg);
533 exit(1);
534 return 0;
536 return res;
539 static struct options_t options[] = {
540 {"o", "output"},
541 {"q", "quality"},
542 {"r", "rate"},
543 {"z", "zlib"},
544 {"M", "mx"},
545 {"x", "xoffset"},
546 {"y", "yoffset"},
547 {"X", "width"},
548 {"Y", "height"},
549 {"T", "flashversion"},
550 {"v", "verbose"},
551 {"V", "version"},
552 {"f", "fit-to-movie"},
553 {"e", "export"},
554 {0,0}
557 int args_callback_longoption(char *name, char *val)
559 return args_long2shortoption(options, name, val);
562 int args_callback_command(char *arg, char *next) // actually used as filename
564 char *s;
565 image_t* i = &image[global.nfiles];
566 if (CheckInputFile(i, arg, &s) < 0) {
567 if (VERBOSE(1))
568 fprintf(stderr, "Unable to open input file: %s\n", arg);
569 free(s);
570 } else {
571 i->filename = s;
572 i->quality = global.quality;
573 global.nfiles++;
574 if (global.nfiles >= MAX_INPUT_FILES) {
575 if (VERBOSE(1))
576 fprintf(stderr, "Error: Too many input files.\n");
577 exit(1);
580 return 0;
583 void args_callback_usage(char *name)
585 printf("\n");
586 printf("Usage: %s [-options [value]] imagefiles[.jpg]|[.jpeg] [...]\n", name);
587 printf("\n");
588 printf("-o , --output <outputfile> Explicitly specify output file. (otherwise, output.swf will be used)\n");
589 printf("-q , --quality <quality> Set compression quality (1-100, 1=worst, 100=best)\n");
590 printf("-r , --rate <framerate> Set movie framerate (frames per second)\n");
591 printf("-z , --zlib <zlib> Enable Flash 6 (MX) Zlib Compression\n");
592 printf("-M , --mx Use Flash MX H.263 compression (use for correlated images)\n");
593 printf("-x , --xoffset <offset> horizontally offset images by <offset>\n");
594 printf("-y , --yoffset <offset> vertically offset images by <offset>\n");
595 printf("-X , --width <width> Force movie width to <width> (default: autodetect)\n");
596 printf("-Y , --height <height> Force movie height to <height> (default: autodetect)\n");
597 printf("-T , --flashversion <version> Set flash file version to <version>\n");
598 printf("-v , --verbose <level> Set verbose level to <level> (0=quiet, 1=default, 2=debug)\n");
599 printf("-V , --version Print version information and exit\n");
600 printf("-f , --fit-to-movie Fit images to movie size\n");
601 printf("-e , --export <assetname> Make importable as asset with <assetname>\n");
602 printf("\n");
606 int main(int argc, char **argv)
608 SWF swf;
609 TAG *t;
611 memset(&global, 0x00, sizeof(global));
613 global.quality = 60;
614 global.framerate = 1.0;
615 global.verbose = 1;
616 global.version = 4;
617 global.asset_name = NULL;
618 global.next_id = 1;
619 global.fit_to_movie = 0;
620 global.scale = 1.0;
622 processargs(argc, argv);
624 if (VERBOSE(2))
625 fprintf(stderr, "Processing %i file(s)...\n", global.nfiles);
627 t = MovieStart(&swf, global.framerate,
628 global.force_width ? global.force_width : (int)(global.max_image_width*global.scale),
629 global.force_height ? global.force_height : (int)(global.max_image_height*global.scale));
632 int i;
633 for (i = 0; i < global.nfiles; i++) {
634 if (VERBOSE(3))
635 fprintf(stderr, "[%03i] %s (%i%%)\n", i,
636 image[i].filename, image[i].quality);
637 t = MovieAddFrame(&swf, t, image[i].filename, image[i].quality,
638 image[i].width, image[i].height);
639 free(image[i].filename);
643 MovieFinish(&swf, t, global.outfile);
645 return 0;