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...
227 if ((fi
= fopen(sname
, "rb")) == NULL
) {
229 fprintf(stderr
, "Read access failed: %s\n", sname
);
234 #if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
235 gft
= DGifOpenFileName(sname
, NULL
);
237 gft
= DGifOpenFileName(sname
);
240 fprintf(stderr
, "%s is not a GIF file!\n", sname
);
244 if ((ret
= DGifSlurp(gft
)) != GIF_OK
) {
245 #if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
246 fprintf(stderr
, "GIF-LIB: %s\n", GifErrorString(ret
));
253 header
.width
= gft
->SWidth
;
254 header
.height
= gft
->SHeight
;
256 pal
= (RGBA
*) malloc(256 * sizeof(RGBA
));
257 memset(pal
, 0, 256 * sizeof(RGBA
));
259 img
= &gft
->SavedImages
[imgidx
].ImageDesc
;
264 // Local colormap has precedence over Global colormap
265 colormap
= img
->ColorMap
? img
->ColorMap
: gft
->SColorMap
;
266 numcolors
= colormap
->ColorCount
;
267 alphapalette
= getTransparentColor(gft
, imgidx
);
269 fprintf(stderr
, "transparent palette index => %d\n", alphapalette
);
270 bpp
= (alphapalette
>= 0 ? 4 : 3);
272 // bgcolor is the background color to fill the bitmap
273 if (gft
->SColorMap
) // There is a GlobalColorMap
274 bgcolor
= (U8
) gft
->SBackGroundColor
; // The SBGColor is meaningful
275 else if (alphapalette
>= 0) // There is a transparency color
276 bgcolor
= alphapalette
; // set the bgcolor to tranparent
279 // Don't know what to do here.
280 // If this doesn't work, we could
281 // create a new color and set the
282 // alpha-channel to transparent
283 // (unless we are using all the 256
284 // colors, in which case either we
285 // give up, or move to 16-bits palette
287 fprintf(stderr
, "BG palette index => %u\n", bgcolor
);
289 for (i
= 0; i
< numcolors
; i
++) {
290 c
= colormap
->Colors
[i
];
291 if (i
== bgcolor
|| i
== alphapalette
)
292 pal
[i
].r
= pal
[i
].g
= pal
[i
].b
= pal
[i
].a
= 0; // Fully transparent
297 pal
[i
].a
= 255; // Fully opaque
301 t
= swf_InsertTag(t
, bpp
== 4 ? ST_DEFINEBITSLOSSLESS2
: ST_DEFINEBITSLOSSLESS
);
302 swf_SetU16(t
, id
); // id
304 // Ah! The Flash specs says scanlines must be DWORD ALIGNED!
305 // (but image width is the correct number of pixels)
306 swf_width
= BYTES_PER_SCANLINE(header
.width
);
308 if ((imagedata
= (U8
*) malloc(swf_width
* header
.height
)) == NULL
) {
309 fprintf(stderr
, "Failed to allocate memory required, aborted.");
314 from
= (U8
*) gft
->SavedImages
[imgidx
].RasterBits
;
316 if (swf_width
== header
.width
) {
317 // we are all nicely aligned and don't need to move the bitmap around.
318 // Just copy the bits into the image buffer.
319 if (!gft
->Image
.Interlace
)
320 if (header
.width
== img
->Width
&& header
.height
== img
->Height
)
321 memcpy(to
, from
, header
.width
* header
.height
);
322 else { //small screen
323 for (i
= 0; i
< header
.height
; i
++, to
+= header
.width
) {
324 memset(to
, bgcolor
, header
.width
);
325 if (i
>= img
->Top
&& i
< img
->Top
+ img
->Height
) {
326 memcpy(to
+ img
->Left
, from
, img
->Width
);
332 else // Need to perform 4 passes on the interlaced images
333 for (i
= 0; i
< 4; i
++)
334 for (j
= interlacedOffset
[i
]; j
< header
.height
;
335 j
+= interlacedJumps
[i
], from
+= header
.width
)
336 memcpy(to
+ header
.width
* j
, from
, header
.width
);
338 padlen
= swf_width
- header
.width
;
340 // here we need to pad the scanline
341 if (!gft
->Image
.Interlace
) {
342 if (header
.width
== img
->Width
&& header
.height
== img
->Height
) {
343 for (i
= 0; i
< header
.height
; i
++, from
+= header
.width
, to
+= swf_width
) {
344 memcpy(to
, from
, header
.width
);
345 memset(to
+ header
.width
, bgcolor
, padlen
);
347 } else { //small screen
348 for (i
= 0; i
< header
.height
; i
++, to
+= swf_width
) {
349 memset(to
, bgcolor
, swf_width
);
350 if (i
>= img
->Top
&& i
< img
->Top
+ img
->Height
) {
351 memcpy(to
+ img
->Left
, from
, img
->Width
);
356 } else { // Need to perform 4 passes on the interlaced images
357 for (i
= 0; i
< 4; i
++)
358 for (j
= interlacedOffset
[i
]; j
< header
.height
;
359 j
+= interlacedJumps
[i
], from
+= header
.width
) {
360 memcpy(to
+ swf_width
* j
, from
, header
.width
);
361 memset(to
+ swf_width
* j
, bgcolor
, padlen
);
365 swf_SetLosslessBitsIndexed(t
, header
.width
, header
.height
, imagedata
, pal
, 256);
367 t
= swf_InsertTag(t
, ST_DEFINESHAPE
);
370 swf_GetMatrix(NULL
, &m
);
373 fs
= swf_ShapeAddBitmapFillStyle(s
, &m
, id
, 0);
375 swf_SetU16(t
, id
+ 1); // id
378 r
.xmax
= header
.width
* 20;
379 r
.ymax
= header
.height
* 20;
382 swf_SetShapeHeader(t
, s
);
384 swf_ShapeSetAll(t
, s
, 0, 0, 0, fs
, 0);
385 swf_ShapeSetLine(t
, s
, r
.xmax
, 0);
386 swf_ShapeSetLine(t
, s
, 0, r
.ymax
);
387 swf_ShapeSetLine(t
, s
, -r
.xmax
, 0);
388 swf_ShapeSetLine(t
, s
, 0, -r
.ymax
);
393 if ((imgidx
> 0) && // REMOVEOBJECT2 not needed at frame 1(imgidx==0)
394 (global
.imagecount
> 1)) {
395 // check last frame's disposal method
396 if ((disposal
= getGifDisposalMethod(gft
, imgidx
- 1)) >= 0) {
399 // [Replace one full-size, non-transparent frame with another]
400 t
= swf_InsertTag(t
, ST_REMOVEOBJECT2
);
401 swf_SetU16(t
, depth
- 1);
403 fprintf(stdout
, " [none]\n");
406 // [Any pixels not covered up by the next frame continue to display]
408 fprintf(stdout
, " [don't dispose]\n");
410 case RESTORE_TO_BGCOLOR
:
411 // [The background color or background tile -rather than a previous frame-
412 // shows through transparent pixels]
413 t
= swf_InsertTag(t
, ST_REMOVEOBJECT2
);
414 swf_SetU16(t
, depth
- 2);
416 fprintf(stdout
, " [restore to bg color]\n");
418 case RESTORE_TO_PREVIOUS
:
419 // [Restores to the state of a previous, undisposed frame]
420 // ** NOT IMPLEMENTED YET (same as "restore to bgcolor") **
421 t
= swf_InsertTag(t
, ST_REMOVEOBJECT2
);
422 swf_SetU16(t
, depth
- 1);
424 fprintf(stdout
, " [restore to previous]\n");
432 swf_SetU16(t
, depth
);
433 t
= swf_InsertTag(t
, ST_PLACEOBJECT2
);
435 swf_GetMatrix(NULL
, &m
);
436 m
.tx
= (swf
->movieSize
.xmax
- (int) header
.width
* 20) / 2;
437 m
.ty
= (swf
->movieSize
.ymax
- (int) header
.height
* 20) / 2;
438 swf_ObjectPlace(t
, id
+ 1, depth
, &m
, NULL
, NULL
);
440 if ((global
.imagecount
> 1) && (global
.loopcount
> 0)) { // 0 means infinite loop
442 SetFrameAction(&t
, AS_FIRSTFRAME
, global
.version
);
445 t
= swf_InsertTag(t
, ST_SHOWFRAME
);
447 if (global
.imagecount
> 1) { // multi-frame GIF?
449 delay
= getGifDelayTime(gft
, imgidx
); // delay in 1/100 sec
450 framecnt
= (int) (global
.framerate
* (delay
/ 100.0));
453 fprintf(stderr
, "at frame %d: pad %d frames(%.3f sec)\n",
454 imgidx
+ 1, framecnt
, delay
/ 100.0);
456 framecnt
-= 1; // already inserted a frame
458 t
= swf_InsertTag(t
, ST_SHOWFRAME
);
460 if ((imgidx
== global
.imagecount
- 1) &&global
.loopcount
> 0) { // last frame
461 as_lastframe
= malloc(strlen(AS_LASTFRAME
) + 5); // 0-99999
462 sprintf(as_lastframe
, AS_LASTFRAME
, global
.loopcount
);
463 SetFrameAction(&t
, as_lastframe
, global
.version
);
476 int CheckInputFile(char *fname
, char **realname
)
479 char *s
= malloc(strlen(fname
) + 5);
488 // Check whether file exists (with typical extensions)
490 if ((fi
= fopen(s
, "rb")) == NULL
) {
491 sprintf(s
, "%s.gif", fname
);
492 if ((fi
= fopen(s
, "rb")) == NULL
) {
493 sprintf(s
, "%s.GIF", fname
);
494 if ((fi
= fopen(s
, "rb")) == NULL
) {
495 sprintf(s
, "%s.Gif", fname
);
496 if ((fi
= fopen(s
, "rb")) == NULL
) {
497 fprintf(stderr
, "Couldn't open %s!\n", fname
);
505 #if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
506 gft
= DGifOpenFileName(s
, NULL
);
508 gft
= DGifOpenFileName(s
);
511 fprintf(stderr
, "%s is not a GIF file!\n", fname
);
515 if (global
.max_image_width
< gft
->SWidth
)
516 global
.max_image_width
= gft
->SWidth
;
517 if (global
.max_image_height
< gft
->SHeight
)
518 global
.max_image_height
= gft
->SHeight
;
520 if ((ret
= DGifSlurp(gft
)) != GIF_OK
) {
521 #if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
522 fprintf(stderr
, "GIF-LIB: %s\n", GifErrorString(ret
));
528 // After DGifSlurp() call, gft->ImageCount become available
529 if ((global
.imagecount
= gft
->ImageCount
) >1) {
530 if (global
.loopcount
< 0) {
531 global
.loopcount
= getGifLoopCount(gft
);
533 fprintf(stderr
, "Loops => %d\n", global
.loopcount
);
538 fprintf(stderr
, "%d x %d, %d images total\n", gft
->SWidth
, gft
->SHeight
, gft
->ImageCount
);
540 for (i
= 0; i
< gft
->ImageCount
; i
++)
541 fprintf(stderr
, "frame: %u, delay: %.3f sec\n", i
+ 1, getGifDelayTime(gft
, i
) / 100.0);
549 int args_callback_option(char *arg
, char *val
)
558 global
.loopcount
= atoi(val
);
564 global
.framerate
= atof(val
);
565 if ((global
.framerate
< 1.0 / 256) ||(global
.framerate
>= 256.0)) {
568 "Error: You must specify a valid framerate between 1/256 and 255.\n");
576 global
.outfile
= val
;
591 global
.verbose
= atoi(val
);
597 global
.force_width
= atoi(val
);
603 global
.force_height
= atoi(val
);
608 printf("gif2swf - part of %s %s\n", PACKAGE
, VERSION
);
618 fprintf(stderr
, "Unknown option: -%s\n", arg
);
625 static struct options_t options
[] = {
638 int args_callback_longoption(char *name
, char *val
)
640 return args_long2shortoption(options
, name
, val
);
643 int args_callback_command(char *arg
, char *next
) // actually used as filename
646 if (CheckInputFile(arg
, &s
) < 0) {
648 fprintf(stderr
, "Error opening input file: %s\n", arg
);
652 image
[global
.nfiles
].filename
= s
;
654 if (global
.nfiles
>= MAX_INPUT_FILES
) {
656 fprintf(stderr
, "Error: Too many input files.\n");
664 void args_callback_usage(char *name
)
667 printf("Usage: %s [-X width] [-Y height] [-o file.swf] [-r rate] file1.gif [file2.gif ...]\n", name
);
669 printf("-r , --rate <framerate> Set movie framerate (frames per second)\n");
670 printf("-o , --output <filename> Set name for SWF output file.\n");
671 printf("-z , --zlib <zlib> Enable Flash 6 (MX) Zlib Compression\n");
672 printf("-l , --loop <loop count> Set loop count. (default: 0 [=infinite loop])\n");
673 printf("-X , --pixel <width> Force movie width to <width> (default: autodetect)\n");
674 printf("-Y , --pixel <height> Force movie height to <height> (default: autodetect)\n");
675 printf("-v , --verbose <level> Set verbose level (0=quiet, 1=default, 2=debug)\n");
676 printf("-C , --cgi For use as CGI- prepend http header, write to stdout\n");
677 printf("-V , --version Print version information and exit\n");
681 int main(int argc
, char **argv
)
686 memset(&global
, 0x00, sizeof(global
));
688 global
.framerate
= 1.0;
691 global
.loopcount
= -1;
693 processargs(argc
, argv
);
695 if (global
.nfiles
<= 0) {
696 fprintf(stderr
, "No gif files found in arguments\n");
701 fprintf(stderr
, "Processing %i file(s)...\n", global
.nfiles
);
703 if (global
.imagecount
> 1) // multi-frame GIF?
704 if (global
.framerate
== 1.0) // user not specified '-r' option?
705 global
.framerate
= 10.0;
707 t
= MovieStart(&swf
, global
.framerate
,
708 global
.force_width
? global
.force_width
: global
.max_image_width
,
709 global
.force_height
? global
.force_height
: global
.max_image_height
);
712 for (i
= 0; i
< global
.nfiles
; i
++) {
714 fprintf(stderr
, "[%03i] %s\n", i
, image
[i
].filename
);
715 t
= MovieAddFrame(&swf
, t
, image
[i
].filename
, (i
* 2) + 1, 0);
716 for (j
= 2; j
<= global
.imagecount
; j
++)
717 t
= MovieAddFrame(&swf
, t
, image
[i
].filename
, (j
* 2) - 1, j
- 1);
718 free(image
[i
].filename
);
722 MovieFinish(&swf
, t
, global
.outfile
);