1 /* -*- mode: c; tab-width: 4; -*- ---------------------------[for (x)emacs]--
3 $Id: gif2swf.c,v 1.7 2008/02/08 11:43:12 kramm Exp $
4 GIF to SWF converter tool
6 Part of the swftools package.
8 Copyright (c) 2005 Daichi Shinozaki <dseg@shield.jp>
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 This file is derived from png2swf.c */
29 #include "../lib/rfxswf.h"
30 #include "../lib/args.h"
32 #define MAX_INPUT_FILES 1024
33 #define VERBOSE(x) (global.verbose>=x)
34 #define AS_FIRSTFRAME "if(!n) n=0;"
35 #define AS_LASTFRAME "if(n<%d){n=n+1;gotoAndPlay(1);}else stop();"
54 } image
[MAX_INPUT_FILES
];
61 enum disposal_method
{
69 void SetFrameAction(TAG
** t
, const char *src
, int ver
)
73 as
= swf_ActionCompile(src
, ver
);
75 fprintf(stderr
, "Couldn't compile ActionScript\n");
77 *t
= swf_InsertTag(*t
, ST_DOACTION
);
78 swf_ActionSet(*t
, as
);
83 int getGifDisposalMethod(GifFileType
* gft
, int framenum
)
86 ExtensionBlock
*ext
= gft
->SavedImages
[framenum
].ExtensionBlocks
;
88 for (i
= 0; i
< gft
->SavedImages
[framenum
].ExtensionBlockCount
; i
++, ext
++)
89 if (ext
->Function
== GRAPHICS_EXT_FUNC_CODE
)
90 return ((ext
->Bytes
[0] & 0x1C) >> 2);
95 int getGifLoopCount(GifFileType
* gft
)
98 ExtensionBlock
*ext
= gft
->SavedImages
[0].ExtensionBlocks
;
100 for (i
= 0; i
< gft
->SavedImages
[0].ExtensionBlockCount
; i
++, ext
++)
101 if (ext
->Function
== APPLICATION_EXT_FUNC_CODE
) {
102 // info: http://semmix.pl/color/exgraf/eeg24.htm
103 if (ext
->ByteCount
== 11 &&
104 (strncmp(&ext
->Bytes
[0], "NETSCAPE2.0", 11) == 0 ||
105 strncmp(&ext
->Bytes
[0], "ANIMEXTS1.0", 11) == 0)) {
106 // check for the subblock
108 if (ext
->ByteCount
!= 3)
111 loop
= GET16(&ext
->Bytes
[1]);
120 U16
getGifDelayTime(GifFileType
* gft
, int framenum
)
123 ExtensionBlock
*ext
= gft
->SavedImages
[framenum
].ExtensionBlocks
;
125 for (i
= 0; i
< gft
->SavedImages
[framenum
].ExtensionBlockCount
; i
++, ext
++)
126 if (ext
->Function
== GRAPHICS_EXT_FUNC_CODE
)
127 return GET16(&ext
->Bytes
[1]);
132 int getTransparentColor(GifFileType
* gft
, int framenum
)
135 ExtensionBlock
*ext
= gft
->SavedImages
[framenum
].ExtensionBlocks
;
137 // Get transparency color from graphic extension block
138 for (i
= 0; i
< gft
->SavedImages
[framenum
].ExtensionBlockCount
; i
++, ext
++)
139 if ((ext
->Function
== GRAPHICS_EXT_FUNC_CODE
) && (ext
->Bytes
[0] & 1)) {
140 // there is a transparent color
141 return ext
->Bytes
[3] == 0 ? 0 : // exception
142 (U8
) ext
->Bytes
[3]; // transparency color
148 TAG
*MovieStart(SWF
* swf
, float framerate
, int dx
, int dy
)
153 memset(swf
, 0x00, sizeof(SWF
));
155 swf
->fileVersion
= global
.version
;
156 swf
->frameRate
= (int) (256.0 * framerate
);
157 swf
->movieSize
.xmax
= dx
* 20;
158 swf
->movieSize
.ymax
= dy
* 20;
160 t
= swf
->firstTag
= swf_InsertTag(NULL
, ST_SETBACKGROUNDCOLOR
);
162 rgb
.r
= rgb
.g
= rgb
.b
= rgb
.a
= 0x00;
164 //rgb.g = 0xff; //<--- handy for testing alpha conversion
170 int MovieFinish(SWF
* swf
, TAG
* t
, char *sname
)
172 int f
, so
= fileno(stdout
);
173 t
= swf_InsertTag(t
, ST_END
);
175 if ((!isatty(so
)) && (!sname
))
179 sname
= "output.swf";
180 f
= open(sname
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, 0644);
185 (swf_WriteCGI(swf
)) fprintf(stderr
, "WriteCGI() failed.\n");
187 if (swf_WriteSWF(f
, swf
) < 0)
188 fprintf(stderr
, "Unable to write output file: %s\n", sname
);
197 TAG
*MovieAddFrame(SWF
* swf
, TAG
* t
, char *sname
, int id
, int imgidx
)
204 U8
*imagedata
, *from
, *to
;
208 struct gif_header header
;
210 int i
, j
, numcolors
, alphapalette
;
212 int bpp
; // byte per pixel
213 int swf_width
, padlen
;
215 ColorMapObject
*colormap
;
217 int interlacedOffset
[] = { 0, 4, 2, 1 }; // The way Interlaced image should
218 int interlacedJumps
[] = { 8, 8, 4, 2 }; // be read - offsets and jumps...
226 if ((fi
= fopen(sname
, "rb")) == NULL
) {
228 fprintf(stderr
, "Read access failed: %s\n", sname
);
233 if ((gft
= DGifOpenFileName(sname
)) == NULL
) {
234 fprintf(stderr
, "%s is not a GIF file!\n", sname
);
238 if (DGifSlurp(gft
) != GIF_OK
) {
243 header
.width
= gft
->SWidth
;
244 header
.height
= gft
->SHeight
;
246 pal
= (RGBA
*) malloc(256 * sizeof(RGBA
));
247 memset(pal
, 0, 256 * sizeof(RGBA
));
249 img
= &gft
->SavedImages
[imgidx
].ImageDesc
;
251 // Local colormap has precedence over Global colormap
252 colormap
= img
->ColorMap
? img
->ColorMap
: gft
->SColorMap
;
253 numcolors
= colormap
->ColorCount
;
254 alphapalette
= getTransparentColor(gft
, imgidx
);
256 fprintf(stderr
, "transparent palette index => %d\n", alphapalette
);
257 bpp
= (alphapalette
>= 0 ? 4 : 3);
259 // bgcolor is the background color to fill the bitmap
260 if (gft
->SColorMap
) // There is a GlobalColorMap
261 bgcolor
= (U8
) gft
->SBackGroundColor
; // The SBGColor is meaningful
262 else if (alphapalette
>= 0) // There is a transparency color
263 bgcolor
= alphapalette
; // set the bgcolor to tranparent
266 // Don't know what to do here.
267 // If this doesn't work, we could
268 // create a new color and set the
269 // alpha-channel to transparent
270 // (unless we are using all the 256
271 // colors, in which case either we
272 // give up, or move to 16-bits palette
274 fprintf(stderr
, "BG palette index => %u\n", bgcolor
);
276 for (i
= 0; i
< numcolors
; i
++) {
277 c
= colormap
->Colors
[i
];
278 if (i
== bgcolor
|| i
== alphapalette
)
279 pal
[i
].r
= pal
[i
].g
= pal
[i
].b
= pal
[i
].a
= 0; // Fully transparent
284 pal
[i
].a
= 255; // Fully opaque
288 t
= swf_InsertTag(t
, bpp
== 4 ? ST_DEFINEBITSLOSSLESS2
: ST_DEFINEBITSLOSSLESS
);
289 swf_SetU16(t
, id
); // id
291 // Ah! The Flash specs says scanlines must be DWORD ALIGNED!
292 // (but image width is the correct number of pixels)
293 swf_width
= BYTES_PER_SCANLINE(header
.width
);
295 if ((imagedata
= (U8
*) malloc(swf_width
* header
.height
)) == NULL
) {
296 fprintf(stderr
, "Failed to allocate memory required, aborted.");
301 from
= (U8
*) gft
->SavedImages
[imgidx
].RasterBits
;
303 if (swf_width
== header
.width
) {
304 // we are all nicely aligned and don't need to move the bitmap around.
305 // Just copy the bits into the image buffer.
306 if (!gft
->Image
.Interlace
)
307 if (header
.width
== img
->Width
&& header
.height
== img
->Height
)
308 memcpy(to
, from
, header
.width
* header
.height
);
309 else { //small screen
310 for (i
= 0; i
< header
.height
; i
++, to
+= header
.width
) {
311 memset(to
, bgcolor
, header
.width
);
312 if (i
>= img
->Top
&& i
< img
->Top
+ img
->Height
) {
313 memcpy(to
+ img
->Left
, from
, img
->Width
);
319 else // Need to perform 4 passes on the interlaced images
320 for (i
= 0; i
< 4; i
++)
321 for (j
= interlacedOffset
[i
]; j
< header
.height
;
322 j
+= interlacedJumps
[i
], from
+= header
.width
)
323 memcpy(to
+ header
.width
* j
, from
, header
.width
);
325 padlen
= swf_width
- header
.width
;
327 // here we need to pad the scanline
328 if (!gft
->Image
.Interlace
) {
329 if (header
.width
== img
->Width
&& header
.height
== img
->Height
) {
330 for (i
= 0; i
< header
.height
; i
++, from
+= header
.width
, to
+= swf_width
) {
331 memcpy(to
, from
, header
.width
);
332 memset(to
+ header
.width
, bgcolor
, padlen
);
334 } else { //small screen
335 for (i
= 0; i
< header
.height
; i
++, to
+= swf_width
) {
336 memset(to
, bgcolor
, swf_width
);
337 if (i
>= img
->Top
&& i
< img
->Top
+ img
->Height
) {
338 memcpy(to
+ img
->Left
, from
, img
->Width
);
343 } else { // Need to perform 4 passes on the interlaced images
344 for (i
= 0; i
< 4; i
++)
345 for (j
= interlacedOffset
[i
]; j
< header
.height
;
346 j
+= interlacedJumps
[i
], from
+= header
.width
) {
347 memcpy(to
+ swf_width
* j
, from
, header
.width
);
348 memset(to
+ swf_width
* j
, bgcolor
, padlen
);
352 swf_SetLosslessBitsIndexed(t
, header
.width
, header
.height
, imagedata
, pal
, 256);
354 t
= swf_InsertTag(t
, ST_DEFINESHAPE
);
357 swf_GetMatrix(NULL
, &m
);
360 fs
= swf_ShapeAddBitmapFillStyle(s
, &m
, id
, 0);
362 swf_SetU16(t
, id
+ 1); // id
365 r
.xmax
= header
.width
* 20;
366 r
.ymax
= header
.height
* 20;
369 swf_SetShapeHeader(t
, s
);
371 swf_ShapeSetAll(t
, s
, 0, 0, 0, fs
, 0);
372 swf_ShapeSetLine(t
, s
, r
.xmax
, 0);
373 swf_ShapeSetLine(t
, s
, 0, r
.ymax
);
374 swf_ShapeSetLine(t
, s
, -r
.xmax
, 0);
375 swf_ShapeSetLine(t
, s
, 0, -r
.ymax
);
380 if ((imgidx
> 0) && // REMOVEOBJECT2 not needed at frame 1(imgidx==0)
381 (global
.imagecount
> 1)) {
382 // check last frame's disposal method
383 if ((disposal
= getGifDisposalMethod(gft
, imgidx
- 1)) >= 0) {
386 // [Replace one full-size, non-transparent frame with another]
387 t
= swf_InsertTag(t
, ST_REMOVEOBJECT2
);
388 swf_SetU16(t
, depth
- 1);
390 fprintf(stdout
, " [none]\n");
393 // [Any pixels not covered up by the next frame continue to display]
395 fprintf(stdout
, " [don't dispose]\n");
397 case RESTORE_TO_BGCOLOR
:
398 // [The background color or background tile -rather than a previous frame-
399 // shows through transparent pixels]
400 t
= swf_InsertTag(t
, ST_REMOVEOBJECT2
);
401 swf_SetU16(t
, depth
- 2);
403 fprintf(stdout
, " [restore to bg color]\n");
405 case RESTORE_TO_PREVIOUS
:
406 // [Restores to the state of a previous, undisposed frame]
407 // ** NOT IMPLEMENTED YET (same as "restore to bgcolor") **
408 t
= swf_InsertTag(t
, ST_REMOVEOBJECT2
);
409 swf_SetU16(t
, depth
- 1);
411 fprintf(stdout
, " [restore to previous]\n");
419 swf_SetU16(t
, depth
);
420 t
= swf_InsertTag(t
, ST_PLACEOBJECT2
);
422 swf_GetMatrix(NULL
, &m
);
423 m
.tx
= (swf
->movieSize
.xmax
- (int) header
.width
* 20) / 2;
424 m
.ty
= (swf
->movieSize
.ymax
- (int) header
.height
* 20) / 2;
425 swf_ObjectPlace(t
, id
+ 1, depth
, &m
, NULL
, NULL
);
427 if ((global
.imagecount
> 1) && (global
.loopcount
> 0)) { // 0 means infinite loop
429 SetFrameAction(&t
, AS_FIRSTFRAME
, global
.version
);
432 t
= swf_InsertTag(t
, ST_SHOWFRAME
);
434 if (global
.imagecount
> 1) { // multi-frame GIF?
436 delay
= getGifDelayTime(gft
, imgidx
); // delay in 1/100 sec
437 framecnt
= (int) (global
.framerate
* (delay
/ 100.0));
440 fprintf(stderr
, "at frame %d: pad %d frames(%.3f sec)\n",
441 imgidx
+ 1, framecnt
, delay
/ 100.0);
443 framecnt
-= 1; // already inserted a frame
445 t
= swf_InsertTag(t
, ST_SHOWFRAME
);
447 if ((imgidx
== global
.imagecount
- 1) &&global
.loopcount
> 0) { // last frame
448 as_lastframe
= malloc(strlen(AS_LASTFRAME
) + 5); // 0-99999
449 sprintf(as_lastframe
, AS_LASTFRAME
, global
.loopcount
);
450 SetFrameAction(&t
, as_lastframe
, global
.version
);
463 int CheckInputFile(char *fname
, char **realname
)
466 char *s
= malloc(strlen(fname
) + 5);
474 // Check whether file exists (with typical extensions)
476 if ((fi
= fopen(s
, "rb")) == NULL
) {
477 sprintf(s
, "%s.gif", fname
);
478 if ((fi
= fopen(s
, "rb")) == NULL
) {
479 sprintf(s
, "%s.GIF", fname
);
480 if ((fi
= fopen(s
, "rb")) == NULL
) {
481 sprintf(s
, "%s.Gif", fname
);
482 if ((fi
= fopen(s
, "rb")) == NULL
) {
483 fprintf(stderr
, "Couldn't open %s!\n", fname
);
491 if ((gft
= DGifOpenFileName(s
)) == NULL
) {
492 fprintf(stderr
, "%s is not a GIF file!\n", fname
);
496 if (global
.max_image_width
< gft
->SWidth
)
497 global
.max_image_width
= gft
->SWidth
;
498 if (global
.max_image_height
< gft
->SHeight
)
499 global
.max_image_height
= gft
->SHeight
;
501 if (DGifSlurp(gft
) != GIF_OK
) {
505 // After DGifSlurp() call, gft->ImageCount become available
506 if ((global
.imagecount
= gft
->ImageCount
) >1) {
507 if (global
.loopcount
< 0) {
508 global
.loopcount
= getGifLoopCount(gft
);
510 fprintf(stderr
, "Loops => %d\n", global
.loopcount
);
515 fprintf(stderr
, "%d x %d, %d images total\n", gft
->SWidth
, gft
->SHeight
, gft
->ImageCount
);
517 for (i
= 0; i
< gft
->ImageCount
; i
++)
518 fprintf(stderr
, "frame: %u, delay: %.3f sec\n", i
+ 1, getGifDelayTime(gft
, i
) / 100.0);
526 int args_callback_option(char *arg
, char *val
)
535 global
.loopcount
= atoi(val
);
541 global
.framerate
= atof(val
);
542 if ((global
.framerate
< 1.0 / 256) ||(global
.framerate
>= 256.0)) {
545 "Error: You must specify a valid framerate between 1/256 and 255.\n");
553 global
.outfile
= val
;
568 global
.verbose
= atoi(val
);
574 global
.force_width
= atoi(val
);
580 global
.force_height
= atoi(val
);
585 printf("gif2swf - part of %s %s\n", PACKAGE
, VERSION
);
595 fprintf(stderr
, "Unknown option: -%s\n", arg
);
602 static struct options_t options
[] = {
615 int args_callback_longoption(char *name
, char *val
)
617 return args_long2shortoption(options
, name
, val
);
620 int args_callback_command(char *arg
, char *next
) // actually used as filename
623 if (CheckInputFile(arg
, &s
) < 0) {
625 fprintf(stderr
, "Error opening input file: %s\n", arg
);
629 image
[global
.nfiles
].filename
= s
;
631 if (global
.nfiles
>= MAX_INPUT_FILES
) {
633 fprintf(stderr
, "Error: Too many input files.\n");
641 void args_callback_usage(char *name
)
644 printf("Usage: %s [-X width] [-Y height] [-o file.swf] [-r rate] file1.gif [file2.gif ...]\n", name
);
646 printf("-r , --rate <framerate> Set movie framerate (frames per second)\n");
647 printf("-o , --output <filename> Set name for SWF output file.\n");
648 printf("-z , --zlib <zlib> Enable Flash 6 (MX) Zlib Compression\n");
649 printf("-l , --loop <loop count> Set loop count. (default: 0 [=infinite loop])\n");
650 printf("-X , --pixel <width> Force movie width to <width> (default: autodetect)\n");
651 printf("-Y , --pixel <height> Force movie height to <height> (default: autodetect)\n");
652 printf("-v , --verbose <level> Set verbose level (0=quiet, 1=default, 2=debug)\n");
653 printf("-C , --cgi For use as CGI- prepend http header, write to stdout\n");
654 printf("-V , --version Print version information and exit\n");
658 int main(int argc
, char **argv
)
663 memset(&global
, 0x00, sizeof(global
));
665 global
.framerate
= 1.0;
668 global
.loopcount
= -1;
670 processargs(argc
, argv
);
672 if (global
.nfiles
<= 0) {
673 fprintf(stderr
, "No gif files found in arguments\n");
678 fprintf(stderr
, "Processing %i file(s)...\n", global
.nfiles
);
680 if (global
.imagecount
> 1) // multi-frame GIF?
681 if (global
.framerate
== 1.0) // user not specified '-r' option?
682 global
.framerate
= 10.0;
684 t
= MovieStart(&swf
, global
.framerate
,
685 global
.force_width
? global
.force_width
: global
.max_image_width
,
686 global
.force_height
? global
.force_height
: global
.max_image_height
);
689 for (i
= 0; i
< global
.nfiles
; i
++) {
691 fprintf(stderr
, "[%03i] %s\n", i
, image
[i
].filename
);
692 t
= MovieAddFrame(&swf
, t
, image
[i
].filename
, (i
* 2) + 1, 0);
693 for (j
= 2; j
<= global
.imagecount
; j
++)
694 t
= MovieAddFrame(&swf
, t
, image
[i
].filename
, (j
* 2) - 1, j
- 1);
695 free(image
[i
].filename
);
699 MovieFinish(&swf
, t
, global
.outfile
);