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)
52 static int custom_move
=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
{
64 image_t image
[MAX_INPUT_FILES
];
68 TAG
*MovieStart(SWF
* swf
, float framerate
, int dx
, int dy
)
73 memset(swf
, 0x00, sizeof(SWF
));
75 swf
->fileVersion
= global
.version
;
76 swf
->frameRate
= (int)(256.0 * framerate
);
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;
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;
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
);
102 swf_SetU16(t
, global
.next_id
++);
108 int MovieFinish(SWF
* swf
, TAG
* t
, char *sname
)
110 int handle
, so
= fileno(stdout
);
112 if (global
.asset_name
) {
115 t
= swf_InsertTag(t
, ST_END
);
116 t
= swf_InsertTag(t
, ST_EXPORTASSETS
);
119 swf_SetString(t
, global
.asset_name
);
121 t
= swf_InsertTag(t
, ST_PLACEOBJECT2
);
122 swf_GetPlaceObject(0, &obj
);
125 swf_SetPlaceObject(t
, &obj
);
127 t
= swf_InsertTag(t
, ST_SHOWFRAME
);
130 t
= swf_InsertTag(t
, ST_END
);
132 if ((!isatty(so
)) && (!sname
))
136 sname
= "output.swf";
137 handle
= open(sname
, O_BINARY
| O_RDWR
| O_CREAT
| O_TRUNC
, 0666);
139 if(handle
<0 && sname
) {
142 if (swf_WriteSWF(handle
, swf
)<0)
143 fprintf(stderr
, "Unable to write output file: %s\n", sname
);
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
;
162 if ((f
=fopen(filename
,"rb"))==NULL
) {
163 fprintf(stderr
, "rfxswf: file open error\n");
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
));
178 *width
= cinfo
.output_width
;
179 *height
= cinfo
.output_height
;
181 for (y
=0;y
<cinfo
.output_height
;y
++) {
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
) {
198 fprintf(stderr
, "Error: Can't convert YCCK to RGB.\n");
200 } else if(cinfo
.out_color_space
== JCS_YCbCr
) {
201 for(x
=0;x
<cinfo
.output_width
;x
++) {
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
);
232 TAG
*MovieAddFrame(SWF
* swf
, TAG
* t
, char *sname
, int quality
,
233 int width
, int height
)
239 int movie_width
= swf
->movieSize
.xmax
- swf
->movieSize
.xmin
;
240 int movie_height
= swf
->movieSize
.ymax
- swf
->movieSize
.ymin
;
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!");
253 t
= swf_InsertTag(t
, ST_VIDEOFRAME
);
254 swf_SetU16(t
, 0xf00d);
255 quant
= 1+(30-(30*quality
)/100);
257 swf_SetVideoStreamIFrame(t
, &stream
, pic2
, quant
);
259 swf_SetVideoStreamPFrame(t
, &stream
, pic2
, quant
);
262 t
= swf_InsertTag(t
, ST_PLACEOBJECT2
);
263 swf_GetPlaceObject(0, &obj
);
272 swf_SetPlaceObject(t
,&obj
);
274 t
= swf_InsertTag(t
, ST_SHOWFRAME
);
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
);
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;
294 fs
= swf_ShapeAddBitmapFillStyle(s
, &m
, global
.next_id
, 1);
296 swf_SetU16(t
, global
.next_id
); // id
299 r
.xmax
= r
.xmin
+ width
* 20;
300 r
.ymax
= r
.ymin
+ height
* 20;
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
);
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
);
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
);
329 t
= swf_InsertTag(t
, ST_SHOWFRAME
);
336 int CheckInputFile(image_t
* i
, char *fname
, char **realname
)
338 struct jpeg_decompress_struct cinfo
;
339 struct jpeg_error_mgr jerr
;
341 char *s
= malloc(strlen(fname
) + 5);
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
)
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
;
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
);
390 int args_callback_option(char *arg
, char *val
)
399 global
.quality
= atoi(val
);
400 if ((global
.quality
< 1) ||(global
.quality
> 100)) {
403 "Error: You must specify a valid quality between 1 and 100.\n");
411 global
.framerate
= atof(val
);
412 if ((global
.framerate
< 1.0/256) || (global
.framerate
>= 256.0)) {
415 "Error: You must specify a valid framerate between 1 and 10000.\n");
423 global
.outfile
= val
;
441 global
.force_width
= atoi(val
);
451 global
.force_height
= atoi(val
);
456 printf("jpeg2swf - part of %s %s\n", PACKAGE
, VERSION
);
461 global
.asset_name
= val
;
466 global
.version
= atoi(val
);
471 global
.fit_to_movie
= 1;
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");
503 char*s
= strdup(val
);
504 char*c
= strchr(s
, ':');
506 fprintf(stderr
, "-m option requires two arguments, <x>:<y>\n");
520 global
.scale
= atof(val
)/100;
532 fprintf(stderr
, "Unknown option: -%s\n", arg
);
539 static struct options_t options
[] = {
549 {"T", "flashversion"},
552 {"f", "fit-to-movie"},
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
565 image_t
* i
= &image
[global
.nfiles
];
566 if (CheckInputFile(i
, arg
, &s
) < 0) {
568 fprintf(stderr
, "Unable to open input file: %s\n", arg
);
572 i
->quality
= global
.quality
;
574 if (global
.nfiles
>= MAX_INPUT_FILES
) {
576 fprintf(stderr
, "Error: Too many input files.\n");
583 void args_callback_usage(char *name
)
586 printf("Usage: %s [-options [value]] imagefiles[.jpg]|[.jpeg] [...]\n", name
);
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");
606 int main(int argc
, char **argv
)
611 memset(&global
, 0x00, sizeof(global
));
614 global
.framerate
= 1.0;
617 global
.asset_name
= NULL
;
619 global
.fit_to_movie
= 0;
622 processargs(argc
, argv
);
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
));
633 for (i
= 0; i
< global
.nfiles
; i
++) {
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
);