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 */
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)
54 static int custom_move=0;
57 static int clip_x1=0,clip_y1=0,clip_x2=0,clip_y2=0;
58 static int custom_clip = 0;
60 typedef struct _image {
66 image_t image[MAX_INPUT_FILES];
70 TAG *MovieStart(SWF * swf, float framerate, int dx, int dy)
75 memset(swf, 0x00, sizeof(SWF));
77 swf->fileVersion = global.version;
78 swf->frameRate = (int)(256.0 * framerate);
81 swf->movieSize.xmin = clip_x1 * 20;
82 swf->movieSize.ymin = clip_y1 * 20;
83 swf->movieSize.xmax = clip_x2 * 20;
84 swf->movieSize.ymax = clip_y2 * 20;
86 swf->movieSize.xmin = global.xoffset * 20;
87 swf->movieSize.ymin = global.yoffset * 20;
88 swf->movieSize.xmax = swf->movieSize.xmin + dx * 20;
89 swf->movieSize.ymax = swf->movieSize.ymin + dy * 20;
92 t = swf->firstTag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
94 rgb.r = rgb.g = rgb.b = rgb.a = 0x00;
98 t = swf_InsertTag(t, ST_DEFINEVIDEOSTREAM);
99 swf_SetU16(t, 0xf00d);
100 swf_SetVideoStreamDefine(t, &stream, 65535, dx, dy);
101 } else if (global.asset_name) {
102 t = swf_InsertTag(t, ST_DEFINESPRITE);
104 swf_SetU16(t, global.next_id++);
110 int MovieFinish(SWF * swf, TAG * t, char *sname)
112 int handle, so = fileno(stdout);
114 if (global.asset_name) {
117 t = swf_InsertTag(t, ST_END);
118 t = swf_InsertTag(t, ST_EXPORTASSETS);
121 swf_SetString(t, global.asset_name);
123 t = swf_InsertTag(t, ST_PLACEOBJECT2);
124 swf_GetPlaceObject(0, &obj);
127 swf_SetPlaceObject(t, &obj);
129 t = swf_InsertTag(t, ST_SHOWFRAME);
132 t = swf_InsertTag(t, ST_END);
134 if ((!isatty(so)) && (!sname))
138 sname = "output.swf";
139 handle = open(sname, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, 0666);
141 if(global.version >= 6) {
142 if (swf_WriteSWC(handle, swf)<0)
143 fprintf(stderr, "Unable to write output file: %s\n", sname);
145 if (swf_WriteSWF(handle, swf)<0)
146 fprintf(stderr, "Unable to write output file: %s\n", sname);
156 int getJPEG(char*filename, int* width, int* height, RGBA**pic2)
158 struct jpeg_decompress_struct cinfo;
159 struct jpeg_error_mgr jerr;
160 struct jpeg_source_mgr mgr;
166 if ((f=fopen(filename,"rb"))==NULL) {
167 fprintf(stderr, "rfxswf: file open error\n");
171 cinfo.err = jpeg_std_error(&jerr);
172 jpeg_create_decompress(&cinfo);
173 jpeg_stdio_src(&cinfo, f);
174 jpeg_read_header(&cinfo, TRUE);
175 jpeg_start_decompress(&cinfo);
177 pic = malloc(cinfo.output_width*cinfo.output_height*sizeof(RGBA));
178 buf = malloc(cinfo.output_width*4);
179 memset(pic, 255, cinfo.output_width*cinfo.output_height*sizeof(RGBA));
182 *width = cinfo.output_width;
183 *height = cinfo.output_height;
185 for (y=0;y<cinfo.output_height;y++) {
187 jpeg_read_scanlines(&cinfo,&buf,1);
189 if(cinfo.out_color_space == JCS_GRAYSCALE) {
190 for(x=0;x<cinfo.output_width;x++) {
191 js[x].r = js[x].g = js[x].b = buf[x];
193 } else if(cinfo.out_color_space == JCS_RGB) {
194 for (x=0;x<cinfo.output_width;x++)
196 js[x].r = buf[x*3+0];
197 js[x].g = buf[x*3+1];
198 js[x].b = buf[x*3+2];
200 } else if(cinfo.out_color_space == JCS_YCCK) {
202 fprintf(stderr, "Error: Can't convert YCCK to RGB.\n");
204 } else if(cinfo.out_color_space == JCS_YCbCr) {
205 for(x=0;x<cinfo.output_width;x++) {
209 js[x].r = y + ((360*(v-128))>>8);
210 js[x].g = y - ((88*(u-128)+183*(v-128))>>8);
211 js[x].b = y + ((455 * (u-128))>>8);
214 else if(cinfo.out_color_space == JCS_CMYK)
216 for(x=0;x<cinfo.output_width;x++) {
217 int white = 255 - buf[x*4+3];
218 js[x].r = white - ((buf[x*4]*white)>>8);
219 js[x].g = white - ((buf[x*4+1]*white)>>8);
220 js[x].b = white - ((buf[x*4+2]*white)>>8);
223 js += cinfo.output_width;
226 jpeg_finish_decompress(&cinfo);
227 jpeg_destroy_decompress(&cinfo);
236 TAG *MovieAddFrame(SWF * swf, TAG * t, char *sname, int quality,
237 int width, int height)
243 int movie_width = swf->movieSize.xmax - swf->movieSize.xmin;
244 int movie_height = swf->movieSize.ymax - swf->movieSize.ymin;
251 getJPEG(sname, &sizex, &sizey, &pic2);
252 if(sizex != stream.owidth || sizey != stream.oheight) {
253 fprintf(stderr, "All images must have the same dimensions if using -m!");
257 t = swf_InsertTag(t, ST_VIDEOFRAME);
258 swf_SetU16(t, 0xf00d);
259 quant = 1+(30-(30*quality)/100);
261 swf_SetVideoStreamIFrame(t, &stream, pic2, quant);
263 swf_SetVideoStreamPFrame(t, &stream, pic2, quant);
266 t = swf_InsertTag(t, ST_PLACEOBJECT2);
267 swf_GetPlaceObject(0, &obj);
276 swf_SetPlaceObject(t,&obj);
278 t = swf_InsertTag(t, ST_SHOWFRAME);
280 t = swf_InsertTag(t, ST_DEFINEBITSJPEG2);
281 swf_SetU16(t, global.next_id); // id
282 swf_SetJPEGBits(t,sname,quality);
284 t = swf_InsertTag(t, ST_DEFINESHAPE);
286 swf_GetMatrix(NULL, &m);
287 if (global.fit_to_movie) {
288 m.sx = 0x10000 * movie_width / width;
289 m.sy = 0x10000 * movie_height / height;
290 width = movie_width / 20;
291 height = movie_height / 20;
296 m.tx = global.xoffset * 20;
297 m.ty = global.yoffset * 20;
298 fs = swf_ShapeAddBitmapFillStyle(s, &m, global.next_id, 1);
300 swf_SetU16(t, global.next_id); // id
301 r.xmin = global.xoffset * 20;
302 r.ymin = global.yoffset * 20;
303 r.xmax = r.xmin + width * 20;
304 r.ymax = r.ymin + height * 20;
306 swf_SetShapeHeader(t, s);
307 swf_ShapeSetAll(t, s, r.xmin, r.ymin, 0, fs, 0);
308 swf_ShapeSetLine(t, s, r.xmax - r.xmin, 0);
309 swf_ShapeSetLine(t, s, 0, r.ymax - r.ymin);
310 swf_ShapeSetLine(t, s, -r.xmax + r.xmin, 0);
311 swf_ShapeSetLine(t, s, 0, -r.ymax + r.ymin);
315 t = swf_InsertTag(t, ST_REMOVEOBJECT2);
316 swf_SetU16(t, 1); // depth
319 t = swf_InsertTag(t, ST_PLACEOBJECT2);
320 swf_GetMatrix(NULL, &m);
321 m.sx = (int)(0x10000 * global.scale);
322 m.sy = (int)(0x10000 * global.scale);
328 m.tx = (movie_width - (width * global.scale * 20)) / 2;
329 m.ty = (movie_height - (height * global.scale * 20)) / 2;
331 swf_ObjectPlace(t, global.next_id, 1, &m, NULL, NULL);
333 t = swf_InsertTag(t, ST_SHOWFRAME);
340 int CheckInputFile(image_t* i, char *fname, char **realname)
342 struct jpeg_decompress_struct cinfo;
343 struct jpeg_error_mgr jerr;
345 char *s = malloc(strlen(fname) + 5);
353 // Check whether file exists (with typical extensions)
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 sprintf(s, "%s.JPG", fname);
361 if ((f = fopen(s, "rb")) == NULL) {
362 sprintf(s, "%s.JPEG", fname);
363 if ((f = fopen(s, "rb")) == NULL)
370 cinfo.err = jpeg_std_error(&jerr);
371 jpeg_create_decompress(&cinfo);
372 jpeg_stdio_src(&cinfo, f);
373 jpeg_read_header(&cinfo, TRUE);
375 width = cinfo.image_width;
376 height = cinfo.image_height;
381 // Get image dimensions
383 if (global.max_image_width < width)
384 global.max_image_width = width;
385 if (global.max_image_height < height)
386 global.max_image_height = height;
388 jpeg_destroy_decompress(&cinfo);
394 int args_callback_option(char *arg, char *val)
403 global.quality = atoi(val);
404 if ((global.quality < 1) ||(global.quality > 100)) {
407 "Error: You must specify a valid quality between 1 and 100.\n");
415 global.framerate = atof(val);
416 if ((global.framerate < 1.0/256) || (global.framerate >= 256.0)) {
419 "Error: You must specify a valid framerate between 1 and 10000.\n");
427 global.outfile = val;
445 global.force_width = atoi(val);
455 global.force_height = atoi(val);
460 printf("jpeg2swf - part of %s %s\n", PACKAGE, VERSION);
465 global.asset_name = val;
471 global.xoffset = atoi(val);
478 global.yoffset = atoi(val);
484 global.fit_to_movie = 1;
489 char*s = strdup(val);
490 char*x1 = strtok(s, ":");
491 char*y1 = strtok(0, ":");
492 char*x2 = strtok(0, ":");
493 char*y2 = strtok(0, ":");
494 if(!(x1 && y1 && x2 && y2)) {
495 fprintf(stderr, "-m option requires four arguments, <x1>:<y1>:<x2>:<y2>\n");
510 char*s = strdup(val);
511 char*c = strchr(s, ':');
513 fprintf(stderr, "-m option requires two arguments, <x>:<y>\n");
527 global.scale = atof(val)/100;
539 fprintf(stderr, "Unknown option: -%s\n", arg);
546 static struct options_t options[] = {
557 {"f", "fit-to-movie"},
562 int args_callback_longoption(char *name, char *val)
564 return args_long2shortoption(options, name, val);
567 int args_callback_command(char *arg, char *next) // actually used as filename
570 image_t* i = &image[global.nfiles];
571 if (CheckInputFile(i, arg, &s) < 0) {
573 fprintf(stderr, "Unable to open input file: %s\n", arg);
577 i->quality = global.quality;
579 if (global.nfiles >= MAX_INPUT_FILES) {
581 fprintf(stderr, "Error: Too many input files.\n");
588 void args_callback_usage(char *name)
591 printf("Usage: %s [-options [value]] imagefiles[.jpg]|[.jpeg] [...]\n", name);
593 printf("-o , --output <outputfile> Explicitly specify output file. (otherwise, output.swf will be used)\n");
594 printf("-q , --quality <quality> Set compression quality (1-100, 1=worst, 100=best)\n");
595 printf("-r , --rate <framerate> Set movie framerate (frames per second)\n");
596 printf("-z , --zlib <zlib> Enable Flash 6 (MX) Zlib Compression\n");
597 printf("-x , --xoffset <offset> horizontally offset images by <offset>\n");
598 printf("-y , --yoffset <offset> vertically offset images by <offset>\n");
599 printf("-X , --width <width> Force movie width to <width> (default: autodetect)\n");
600 printf("-Y , --height <height> Force movie height to <height> (default: autodetect)\n");
601 printf("-v , --verbose <level> Set verbose level to <level> (0=quiet, 1=default, 2=debug)\n");
602 printf("-V , --version Print version information and exit\n");
603 printf("-f , --fit-to-movie Fit images to movie size\n");
604 printf("-e , --export <assetname> Make importable as asset with <assetname>\n");
609 int main(int argc, char **argv)
614 memset(&global, 0x00, sizeof(global));
617 global.framerate = 1.0;
620 global.asset_name = NULL;
624 global.fit_to_movie = 0;
627 processargs(argc, argv);
630 fprintf(stderr, "Processing %i file(s)...\n", global.nfiles);
632 t = MovieStart(&swf, global.framerate,
633 global.force_width ? global.force_width : (int)(global.max_image_width*global.scale),
634 global.force_height ? global.force_height : (int)(global.max_image_height*global.scale));
638 for (i = 0; i < global.nfiles; i++) {
640 fprintf(stderr, "[%03i] %s (%i%%, 1/%i)\n", i,
641 image[i].filename, image[i].quality);
642 t = MovieAddFrame(&swf, t, image[i].filename, image[i].quality,
643 image[i].width, image[i].height);
644 free(image[i].filename);
648 MovieFinish(&swf, t, global.outfile);