2 SDL_anigif: An example animated GIF image loading library for use with SDL
3 SDL_image Copyright (C) 1997-2006 Sam Lantinga
4 Animated GIF "derived work" Copyright (C) 2006 Doug McFadyen
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "SDL_anigif.h"
28 /* Code from here to end of file has been adapted from XPaint: */
29 /* +-------------------------------------------------------------------+ */
30 /* | Copyright 1990, 1991, 1993 David Koblas. | */
31 /* | Copyright 1996 Torsten Martinsen. | */
32 /* | Permission to use, copy, modify, and distribute this software | */
33 /* | and its documentation for any purpose and without fee is hereby | */
34 /* | granted, provided that the above copyright notice appear in all | */
35 /* | copies and that both that copyright notice and this permission | */
36 /* | notice appear in supporting documentation. This software is | */
37 /* | provided "as is" without express or implied warranty. | */
38 /* +-------------------------------------------------------------------+ */
39 /* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */
40 /* Animated GIF support by Doug McFadyen -- 10/19/06 */
42 #define MAXCOLORMAPSIZE 256
51 #define MAX_LWZ_BITS 12
53 #define INTERLACE 0x40
54 #define LOCALCOLORMAP 0x80
55 #define BitSet(byte,bit) (((byte) & (bit)) == (bit))
56 #define LM_to_uint(a,b) (((b)<<8)|(a))
58 #define SDL_SetError(t) ((void)0) /* We're not SDL so ignore error reporting */
65 unsigned char ColorMap[3][MAXCOLORMAPSIZE];
66 unsigned int BitPixel;
67 unsigned int ColorResolution;
68 unsigned int Background;
69 unsigned int AspectRatio;
87 /* AG_LoadGIF_RW data */
88 unsigned char localColorMap[3][MAXCOLORMAPSIZE];
90 unsigned char buf[280];
91 int curbit, lastbit, done, lastbyte;
92 /* LWZReadByte data */
93 int fresh, code, incode;
94 int codesize, setcodesize;
95 int maxcode, maxcodesize;
96 int firstcode, oldcode;
97 int clearcode, endcode;
98 int table[2][(1 << MAX_LWZ_BITS)];
99 int stack[(1 << (MAX_LWZ_BITS))*2], *sp;
104 static int ReadColorMap( gifdata* gd, int number, unsigned char buffer[3][MAXCOLORMAPSIZE] );
105 static int DoExtension( gifdata* gd, int label );
106 static int GetDataBlock( gifdata* gd, unsigned char* buf );
107 static int GetCode( gifdata* gd, int code_size, int flag );
108 static int LWZReadByte( gifdata* gd, int flag, int input_code_size );
109 static SDL_Surface* ReadImage( gifdata* gd, int len, int height, int, unsigned char cmap[3][MAXCOLORMAPSIZE], int interlace, int ignore );
113 /*--------------------------------------------------------------------------*
115 *--------------------------------------------------------------------------*/
116 int AG_isGIF( SDL_RWops* src )
122 int start = SDL_RWtell( src );
125 if ( SDL_RWread(src,magic,sizeof(magic),1) )
127 if ( (strncmp(magic,"GIF",3) == 0) && ((memcmp(magic+3,"87a",3) == 0) || (memcmp(magic+3,"89a",3) == 0)) )
133 SDL_RWseek( src, start, SEEK_SET );
141 /*--------------------------------------------------------------------------*
143 *--------------------------------------------------------------------------*/
144 int AG_LoadGIF( const char* file, AG_Frame* frames, int size )
148 SDL_RWops* src = SDL_RWFromFile( file, "rb" );
152 n = AG_LoadGIF_RW( src, frames, size );
161 /*--------------------------------------------------------------------------*
163 *--------------------------------------------------------------------------*/
164 void AG_FreeSurfaces( AG_Frame* frames, int nFrames )
170 for ( i = 0; i < nFrames; i++ )
172 if ( frames[i].surface )
174 SDL_FreeSurface( frames[i].surface );
175 frames[i].surface = NULL;
183 /*--------------------------------------------------------------------------*
185 *--------------------------------------------------------------------------*/
186 int AG_ConvertSurfacesToDisplayFormat( AG_Frame* frames, int nFrames )
193 for ( i = 0; i < nFrames; i++ )
195 if ( frames[i].surface )
197 SDL_Surface* surface = (frames[i].surface->flags & SDL_SRCCOLORKEY) ? SDL_DisplayFormatAlpha(frames[i].surface) : SDL_DisplayFormat(frames[i].surface);
201 SDL_FreeSurface( frames[i].surface );
202 frames[i].surface = surface;
214 /*--------------------------------------------------------------------------*
216 *--------------------------------------------------------------------------*/
217 int AG_NormalizeSurfacesToDisplayFormat( AG_Frame* frames, int nFrames )
221 if ( nFrames > 0 && frames && frames[0].surface )
223 SDL_Surface* mainSurface = (frames[0].surface->flags & SDL_SRCCOLORKEY) ? SDL_DisplayFormatAlpha(frames[0].surface) : SDL_DisplayFormat(frames[0].surface);
224 const int newDispose = (frames[0].surface->flags & SDL_SRCCOLORKEY) ? AG_DISPOSE_RESTORE_BACKGROUND : AG_DISPOSE_NONE;
229 int lastDispose = AG_DISPOSE_NA;
231 const Uint8 alpha = (frames[0].disposal == AG_DISPOSE_NONE) ? SDL_ALPHA_OPAQUE : SDL_ALPHA_TRANSPARENT;
233 SDL_FillRect( mainSurface, NULL, SDL_MapRGBA(mainSurface->format,0,0,0,alpha) );
235 for ( i = 0; i < nFrames; i++ )
237 if ( frames[i].surface )
239 SDL_Surface* surface = SDL_ConvertSurface( mainSurface, mainSurface->format, mainSurface->flags );
245 if ( lastDispose == AG_DISPOSE_NONE )
246 SDL_BlitSurface( frames[i-1].surface, NULL, surface, NULL );
248 if ( lastDispose == AG_DISPOSE_RESTORE_PREVIOUS )
249 SDL_BlitSurface( frames[iRestore].surface, NULL, surface, NULL );
250 if ( frames[i].disposal != AG_DISPOSE_RESTORE_PREVIOUS )
253 r.x = (Sint16)frames[i].x;
254 r.y = (Sint16)frames[i].y;
255 SDL_BlitSurface( frames[i].surface, NULL, surface, &r );
257 SDL_FreeSurface( frames[i].surface );
258 frames[i].surface = surface;
259 frames[i].x = frames[i].y = 0;
260 lastDispose = frames[i].disposal;
261 frames[i].disposal = newDispose;
267 SDL_FreeSurface( mainSurface );
276 /*--------------------------------------------------------------------------*
278 *--------------------------------------------------------------------------*/
279 int AG_LoadGIF_RW( SDL_RWops* src, AG_Frame* frames, int maxFrames )
282 unsigned char buf[16];
284 int useGlobalColormap;
288 SDL_Surface* image = NULL;
294 gd = (gifdata*)malloc( sizeof(*gd) );
295 memset( gd, 0, sizeof(*gd) );
298 start = SDL_RWtell( src );
300 if ( !SDL_RWread(src,buf,6,1) )
302 SDL_SetError( "error reading magic number" );
306 if ( strncmp((char*)buf,"GIF",3) != 0 )
308 SDL_SetError( "not a GIF file" );
312 strncpy( version, (char*)buf+3, 3 );
315 if ( (strcmp(version,"87a") != 0) && (strcmp(version,"89a") != 0) )
317 SDL_SetError( "bad version number, not '87a' or '89a'" );
321 gd->g89.transparent = -1;
322 gd->g89.delayTime = -1;
323 gd->g89.inputFlag = -1;
324 gd->g89.disposal = AG_DISPOSE_NA;
326 if ( !SDL_RWread(src,buf,7,1) )
328 SDL_SetError( "failed to read screen descriptor" );
332 gd->gs.Width = LM_to_uint(buf[0],buf[1]);
333 gd->gs.Height = LM_to_uint(buf[2],buf[3]);
334 gd->gs.BitPixel = 2 << (buf[4] & 0x07);
335 gd->gs.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
336 gd->gs.Background = buf[5];
337 gd->gs.AspectRatio = buf[6];
339 if ( BitSet(buf[4],LOCALCOLORMAP) ) /* Global Colormap */
341 if ( ReadColorMap(gd,gd->gs.BitPixel,gd->gs.ColorMap) )
343 SDL_SetError( "error reading global colormap" );
350 if ( !SDL_RWread(src,&c,1,1) )
352 SDL_SetError( "EOF / read error on image data" );
356 if ( c == ';' ) /* GIF terminator */
359 if ( c == '!' ) /* Extension */
361 if ( !SDL_RWread(src,&c,1,1) )
363 SDL_SetError( "EOF / read error on extention function code" );
366 DoExtension( gd, c );
370 if ( c != ',' ) /* Not a valid start character */
373 if ( !SDL_RWread(src,buf,9,1) )
375 SDL_SetError( "couldn't read left/top/width/height" );
379 useGlobalColormap = !BitSet(buf[8],LOCALCOLORMAP);
380 bitPixel = 1 << ((buf[8] & 0x07) + 1);
382 if ( !useGlobalColormap )
384 if ( ReadColorMap(gd,bitPixel,gd->localColorMap) )
386 SDL_SetError( "error reading local colormap" );
389 image = ReadImage( gd, LM_to_uint(buf[4],buf[5]), LM_to_uint(buf[6],buf[7]), bitPixel, gd->localColorMap, BitSet(buf[8],INTERLACE), (frames==NULL) );
393 image = ReadImage( gd, LM_to_uint(buf[4],buf[5]), LM_to_uint(buf[6],buf[7]), gd->gs.BitPixel, gd->gs.ColorMap, BitSet(buf[8],INTERLACE), (frames==NULL) );
401 if ( gd->g89.transparent >= 0 )
402 SDL_SetColorKey( image, SDL_SRCCOLORKEY, gd->g89.transparent );
404 frames[iFrame].surface = image;
405 frames[iFrame].x = LM_to_uint(buf[0], buf[1]);
406 frames[iFrame].y = LM_to_uint(buf[2], buf[3]);
407 frames[iFrame].disposal = gd->g89.disposal;
408 frames[iFrame].delay = gd->g89.delayTime*10;
409 /* gd->g89.transparent = -1; ** Hmmm, not sure if this should be reset for each frame? */
413 } while ( iFrame < maxFrames || frames == NULL );
417 SDL_RWseek( src, start, SEEK_SET );
426 /*--------------------------------------------------------------------------*
428 *--------------------------------------------------------------------------*/
429 static int ReadColorMap( gifdata* gd, int number, unsigned char buffer[3][MAXCOLORMAPSIZE] )
432 unsigned char rgb[3];
437 for ( i = 0; i < number; ++i )
439 if ( !SDL_RWread(gd->src,rgb,sizeof(rgb),1) )
441 SDL_SetError( "bad colormap" );
445 buffer[CM_RED][i] = rgb[0];
446 buffer[CM_GREEN][i] = rgb[1];
447 buffer[CM_BLUE][i] = rgb[2];
448 flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
456 /*--------------------------------------------------------------------------*
458 *--------------------------------------------------------------------------*/
459 static int DoExtension( gifdata* gd, int label )
461 unsigned char buf[256];
465 case 0x01: /* Plain Text Extension */
468 case 0xff: /* Application Extension */
471 case 0xfe: /* Comment Extension */
472 while ( GetDataBlock(gd,buf) != 0 )
476 case 0xf9: /* Graphic Control Extension */
477 (void)GetDataBlock( gd, buf );
478 gd->g89.disposal = (buf[0] >> 2) & 0x7;
479 gd->g89.inputFlag = (buf[0] >> 1) & 0x1;
480 gd->g89.delayTime = LM_to_uint(buf[1],buf[2]);
481 if ( (buf[0] & 0x1) != 0 )
482 gd->g89.transparent = buf[3];
484 while ( GetDataBlock(gd,buf) != 0 )
489 while ( GetDataBlock(gd,buf) != 0 )
497 /*--------------------------------------------------------------------------*
499 *--------------------------------------------------------------------------*/
500 static int GetDataBlock( gifdata* gd, unsigned char* buf )
504 if ( !SDL_RWread(gd->src,&count,1,1) )
506 /* pm_message("error in getting DataBlock size" ); */
510 gd->zerodatablock = count == 0;
512 if ( (count != 0) && !SDL_RWread(gd->src,buf,count,1) )
514 /* pm_message("error in reading DataBlock" ); */
523 /*--------------------------------------------------------------------------*
525 *--------------------------------------------------------------------------*/
526 static int GetCode( gifdata* gd, int code_size, int flag )
539 if ( (gd->curbit + code_size) >= gd->lastbit )
543 if ( gd->curbit >= gd->lastbit )
544 SDL_SetError( "ran off the end of my bits" );
548 gd->buf[0] = gd->buf[gd->lastbyte - 2];
549 gd->buf[1] = gd->buf[gd->lastbyte - 1];
551 if ( (count = GetDataBlock(gd, &gd->buf[2])) == 0 )
554 gd->lastbyte = 2 + count;
555 gd->curbit = (gd->curbit - gd->lastbit) + 16;
556 gd->lastbit = (2 + count)*8;
560 for ( i = gd->curbit, j = 0; j < code_size; ++i, ++j )
561 ret |= ((gd->buf[i / 8] & (1 << (i % 8))) != 0) << j;
563 gd->curbit += code_size;
570 /*--------------------------------------------------------------------------*
572 *--------------------------------------------------------------------------*/
573 static int LWZReadByte( gifdata* gd, int flag, int input_code_size )
579 gd->setcodesize = input_code_size;
580 gd->codesize = gd->setcodesize + 1;
581 gd->clearcode = 1 << gd->setcodesize;
582 gd->endcode = gd->clearcode + 1;
583 gd->maxcodesize = gd->clearcode*2;
584 gd->maxcode = gd->clearcode + 2;
586 GetCode( gd, 0, TRUE );
590 for ( i = 0; i < gd->clearcode; ++i )
596 for ( ; i < (1 << MAX_LWZ_BITS); ++i )
597 gd->table[0][i] = gd->table[1][0] = 0;
602 else if ( gd->fresh )
607 gd->firstcode = gd->oldcode = GetCode( gd, gd->codesize, FALSE );
608 } while ( gd->firstcode == gd->clearcode );
609 return gd->firstcode;
612 if ( gd->sp > gd->stack )
615 while ( (code = GetCode(gd,gd->codesize,FALSE)) >= 0 )
617 if ( code == gd->clearcode )
619 for ( i = 0; i < gd->clearcode; ++i )
625 for ( ; i < (1 << MAX_LWZ_BITS); ++i )
626 gd->table[0][i] = gd->table[1][i] = 0;
628 gd->codesize = gd->setcodesize + 1;
629 gd->maxcodesize = gd->clearcode*2;
630 gd->maxcode = gd->clearcode + 2;
632 gd->firstcode = gd->oldcode = GetCode( gd, gd->codesize, FALSE );
633 return gd->firstcode;
635 else if ( code == gd->endcode )
638 unsigned char buf[260];
640 if ( gd->zerodatablock )
643 while ( (count = GetDataBlock(gd,buf)) > 0 )
648 /* pm_message("missing EOD in data stream (common occurence)"); */
655 if ( code >= gd->maxcode )
657 *gd->sp++ = gd->firstcode;
661 while ( code >= gd->clearcode )
663 *gd->sp++ = gd->table[1][code];
664 if ( code == gd->table[0][code] )
665 SDL_SetError( "circular table entry BIG ERROR" );
666 code = gd->table[0][code];
669 *gd->sp++ = gd->firstcode = gd->table[1][code];
671 if ( (code = gd->maxcode) < (1 << MAX_LWZ_BITS) )
673 gd->table[0][code] = gd->oldcode;
674 gd->table[1][code] = gd->firstcode;
676 if ( (gd->maxcode >= gd->maxcodesize) && (gd->maxcodesize < (1 << MAX_LWZ_BITS)) )
678 gd->maxcodesize *= 2;
683 gd->oldcode = incode;
685 if ( gd->sp > gd->stack )
694 /*--------------------------------------------------------------------------*
696 *--------------------------------------------------------------------------*/
697 static SDL_Surface* ReadImage( gifdata* gd, int len, int height, int cmapSize, unsigned char cmap[3][MAXCOLORMAPSIZE], int interlace, int ignore )
702 int xpos = 0, ypos = 0, pass = 0;
704 /* Initialize the compression routines */
705 if ( !SDL_RWread(gd->src,&c,1,1) )
707 SDL_SetError( "EOF / read error on image data" );
711 if ( LWZReadByte(gd,TRUE,c) < 0 )
713 SDL_SetError( "error reading image" );
717 /* If this is an "uninteresting picture" ignore it. */
720 while ( LWZReadByte(gd,FALSE,c) >= 0 )
725 image = SDL_AllocSurface( SDL_SWSURFACE, len, height, 8, 0, 0, 0, 0 );
727 for ( i = 0; i < cmapSize; i++ )
729 image->format->palette->colors[i].r = cmap[CM_RED][i];
730 image->format->palette->colors[i].g = cmap[CM_GREEN][i];
731 image->format->palette->colors[i].b = cmap[CM_BLUE][i];
734 while ( (v = LWZReadByte(gd,FALSE,c)) >= 0 )
736 ((Uint8*)image->pixels)[xpos + ypos*image->pitch] = (Uint8)v;
747 case 1: ypos += 8; break;
748 case 2: ypos += 4; break;
749 case 3: ypos += 2; break;
752 if ( ypos >= height )
757 case 1: ypos = 4; break;
758 case 2: ypos = 2; break;
759 case 3: ypos = 1; break;
770 if ( ypos >= height )