steed/src/sdl-instead/SDL_anigif.c
2011-04-19 10:57:52 +00:00

749 lines
16 KiB
C

/*
SDL_anigif: An example animated GIF image loading library for use with SDL
SDL_image Copyright (C) 1997-2006 Sam Lantinga
Animated GIF "derived work" Copyright (C) 2006 Doug McFadyen
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <string.h>
#include "SDL_anigif.h"
#include "sdl_idf.h"
extern idf_t game_idf;
/* Code from here to end of file has been adapted from XPaint: */
/* +-------------------------------------------------------------------+ */
/* | Copyright 1990, 1991, 1993 David Koblas. | */
/* | Copyright 1996 Torsten Martinsen. | */
/* | Permission to use, copy, modify, and distribute this software | */
/* | and its documentation for any purpose and without fee is hereby | */
/* | granted, provided that the above copyright notice appear in all | */
/* | copies and that both that copyright notice and this permission | */
/* | notice appear in supporting documentation. This software is | */
/* | provided "as is" without express or implied warranty. | */
/* +-------------------------------------------------------------------+ */
/* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */
/* Animated GIF support by Doug McFadyen -- 10/19/06 */
#define MAXCOLORMAPSIZE 256
#define TRUE 1
#define FALSE 0
#define CM_RED 0
#define CM_GREEN 1
#define CM_BLUE 2
#define MAX_LWZ_BITS 12
#define INTERLACE 0x40
#define LOCALCOLORMAP 0x80
#define BitSet(byte,bit) (((byte) & (bit)) == (bit))
#define LM_to_uint(a,b) (((b)<<8)|(a))
#define SDL_SetError(t) ((void)0) /* We're not SDL so ignore error reporting */
typedef struct
{
unsigned int Width;
unsigned int Height;
unsigned char ColorMap[3][MAXCOLORMAPSIZE];
unsigned int BitPixel;
unsigned int ColorResolution;
unsigned int Background;
unsigned int AspectRatio;
} gifscreen;
typedef struct
{
int transparent;
int delayTime;
int inputFlag;
int disposal;
} gif89;
typedef struct
{
/* global data */
SDL_RWops* src;
int loop;
gifscreen gs;
gif89 g89;
int zerodatablock;
/* AG_LoadGIF_RW data */
unsigned char localColorMap[3][MAXCOLORMAPSIZE];
/* GetCode data */
unsigned char buf[280];
int curbit, lastbit, done, lastbyte;
/* LWZReadByte data */
int fresh, code, incode;
int codesize, setcodesize;
int maxcode, maxcodesize;
int firstcode, oldcode;
int clearcode, endcode;
int table[2][(1 << MAX_LWZ_BITS)];
int stack[(1 << (MAX_LWZ_BITS))*2], *sp;
} gifdata;
static int ReadColorMap( gifdata* gd, int number, unsigned char buffer[3][MAXCOLORMAPSIZE] );
static int DoExtension( gifdata* gd, int label );
static int GetDataBlock( gifdata* gd, unsigned char* buf );
static int GetCode( gifdata* gd, int code_size, int flag );
static int LWZReadByte( gifdata* gd, int flag, int input_code_size );
static SDL_Surface* ReadImage( gifdata* gd, int len, int height, int, unsigned char cmap[3][MAXCOLORMAPSIZE], int interlace, int ignore );
int AG_isGIF( SDL_RWops* src )
{
int isGIF = FALSE;
if ( src )
{
int start = SDL_RWtell( src );
char magic[6];
if ( SDL_RWread(src,magic,sizeof(magic),1) )
{
if ( (strncmp(magic,"GIF",3) == 0) && ((memcmp(magic+3,"87a",3) == 0) || (memcmp(magic+3,"89a",3) == 0)) )
{
isGIF = TRUE;
}
}
SDL_RWseek( src, start, SEEK_SET );
}
return isGIF;
}
int AG_LoadGIF( const char* file, AG_Frame* frames, int size, int *loop )
{
int n = 0;
SDL_RWops* src = RWFromIdf(game_idf, file);
if ( src )
{
n = AG_LoadGIF_RW( src, frames, size, loop );
SDL_RWclose( src );
}
return n;
}
void AG_FreeSurfaces( AG_Frame* frames, int nFrames )
{
int i;
if ( frames )
{
for ( i = 0; i < nFrames; i++ )
{
if ( frames[i].surface )
{
SDL_FreeSurface( frames[i].surface );
frames[i].surface = NULL;
}
}
}
}
int AG_ConvertSurfacesToDisplayFormat( AG_Frame* frames, int nFrames )
{
int i;
int n = 0;
if ( frames )
{
for ( i = 0; i < nFrames; i++ )
{
if ( frames[i].surface )
{
SDL_Surface* surface = (frames[i].surface->flags & SDL_SRCCOLORKEY) ? SDL_DisplayFormatAlpha(frames[i].surface) : SDL_DisplayFormat(frames[i].surface);
if ( surface )
{
SDL_FreeSurface( frames[i].surface );
frames[i].surface = surface;
n++;
}
}
}
}
return n;
}
int AG_NormalizeSurfacesToDisplayFormat( AG_Frame* frames, int nFrames )
{
int n = 0;
if ( nFrames > 0 && frames && frames[0].surface )
{
SDL_Surface* mainSurface = (frames[0].surface->flags & SDL_SRCCOLORKEY) ? SDL_DisplayFormatAlpha(frames[0].surface) : SDL_DisplayFormat(frames[0].surface);
const int newDispose = (frames[0].surface->flags & SDL_SRCCOLORKEY) ? AG_DISPOSE_RESTORE_BACKGROUND : AG_DISPOSE_NONE;
if ( mainSurface )
{
int i;
int lastDispose = AG_DISPOSE_NA;
int iRestore = 0;
const Uint8 alpha = (frames[0].disposal == AG_DISPOSE_NONE ||
frames[0].disposal == AG_DISPOSE_NA) ? SDL_ALPHA_OPAQUE : SDL_ALPHA_TRANSPARENT;
SDL_FillRect( mainSurface, NULL, SDL_MapRGBA(mainSurface->format,0,0,0,alpha) );
for ( i = 0; i < nFrames; i++ )
{
if ( frames[i].surface )
{
SDL_Surface* surface = SDL_ConvertSurface( mainSurface, mainSurface->format, mainSurface->flags );
if ( surface )
{
SDL_Rect r;
if ( lastDispose == AG_DISPOSE_NONE )
SDL_BlitSurface( frames[i-1].surface, NULL, surface, NULL );
if ( lastDispose == AG_DISPOSE_RESTORE_PREVIOUS )
SDL_BlitSurface( frames[iRestore].surface, NULL, surface, NULL );
if ( frames[i].disposal != AG_DISPOSE_RESTORE_PREVIOUS )
iRestore = i;
r.x = (Sint16)frames[i].x;
r.y = (Sint16)frames[i].y;
SDL_BlitSurface( frames[i].surface, NULL, surface, &r );
SDL_FreeSurface( frames[i].surface );
frames[i].surface = surface;
frames[i].x = frames[i].y = 0;
lastDispose = frames[i].disposal;
frames[i].disposal = newDispose;
n++;
}
}
}
SDL_FreeSurface( mainSurface );
}
}
return n;
}
int AG_LoadGIF_RW( SDL_RWops* src, AG_Frame* frames, int maxFrames, int *loop)
{
int start;
unsigned char buf[16];
unsigned char c;
int useGlobalColormap;
int bitPixel;
int iFrame = 0;
char version[4];
SDL_Surface* image = NULL;
gifdata* gd;
if ( src == NULL )
return 0;
gd = malloc( sizeof(*gd) );
memset( gd, 0, sizeof(*gd) );
gd->src = src;
gd->loop = 1;
start = SDL_RWtell( src );
if ( !SDL_RWread(src,buf,6,1) )
{
SDL_SetError( "error reading magic number" );
goto done;
}
if ( strncmp((char*)buf,"GIF",3) != 0 )
{
SDL_SetError( "not a GIF file" );
goto done;
}
strncpy( version, (char*)buf+3, 3 );
version[3] = '\0';
if ( (strcmp(version,"87a") != 0) && (strcmp(version,"89a") != 0) )
{
SDL_SetError( "bad version number, not '87a' or '89a'" );
goto done;
}
gd->g89.transparent = -1;
gd->g89.delayTime = -1;
gd->g89.inputFlag = -1;
gd->g89.disposal = AG_DISPOSE_NA;
if ( !SDL_RWread(src,buf,7,1) )
{
SDL_SetError( "failed to read screen descriptor" );
goto done;
}
gd->gs.Width = LM_to_uint(buf[0],buf[1]);
gd->gs.Height = LM_to_uint(buf[2],buf[3]);
gd->gs.BitPixel = 2 << (buf[4] & 0x07);
gd->gs.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
gd->gs.Background = buf[5];
gd->gs.AspectRatio = buf[6];
if ( BitSet(buf[4],LOCALCOLORMAP) ) /* Global Colormap */
{
if ( ReadColorMap(gd,gd->gs.BitPixel,gd->gs.ColorMap) )
{
SDL_SetError( "error reading global colormap" );
goto done;
}
}
do
{
if ( !SDL_RWread(src,&c,1,1) )
{
SDL_SetError( "EOF / read error on image data" );
goto done;
}
if ( c == ';' ) /* GIF terminator */
goto done;
if ( c == '!' ) /* Extension */
{
if ( !SDL_RWread(src,&c,1,1) )
{
SDL_SetError( "EOF / read error on extention function code" );
goto done;
}
DoExtension( gd, c );
continue;
}
if ( c != ',' ) /* Not a valid start character */
continue;
if ( !SDL_RWread(src,buf,9,1) )
{
SDL_SetError( "couldn't read left/top/width/height" );
goto done;
}
useGlobalColormap = !BitSet(buf[8],LOCALCOLORMAP);
bitPixel = 1 << ((buf[8] & 0x07) + 1);
if ( !useGlobalColormap )
{
if ( ReadColorMap(gd,bitPixel,gd->localColorMap) )
{
SDL_SetError( "error reading local colormap" );
goto done;
}
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) );
}
else
{
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) );
}
if ( frames )
{
if ( image == NULL )
goto done;
if ( gd->g89.transparent >= 0 )
SDL_SetColorKey( image, SDL_SRCCOLORKEY, gd->g89.transparent );
frames[iFrame].surface = image;
frames[iFrame].x = LM_to_uint(buf[0], buf[1]);
frames[iFrame].y = LM_to_uint(buf[2], buf[3]);
frames[iFrame].disposal = gd->g89.disposal;
frames[iFrame].delay = gd->g89.delayTime*10;
/* gd->g89.transparent = -1; ** Hmmm, not sure if this should be reset for each frame? */
}
iFrame++;
} while ( iFrame < maxFrames || frames == NULL );
done:
if ( image == NULL )
SDL_RWseek( src, start, SEEK_SET );
if (loop)
*loop = gd->loop;
free( gd );
return iFrame;
}
static int ReadColorMap( gifdata* gd, int number, unsigned char buffer[3][MAXCOLORMAPSIZE] )
{
int i;
unsigned char rgb[3];
int flag;
flag = TRUE;
for ( i = 0; i < number; ++i )
{
if ( !SDL_RWread(gd->src,rgb,sizeof(rgb),1) )
{
SDL_SetError( "bad colormap" );
return 1;
}
buffer[CM_RED][i] = rgb[0];
buffer[CM_GREEN][i] = rgb[1];
buffer[CM_BLUE][i] = rgb[2];
flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
}
return FALSE;
}
static int DoExtension( gifdata* gd, int label )
{
unsigned char buf[256];
switch ( label )
{
case 0x01: /* Plain Text Extension */
break;
case 0xff: /* Application Extension */
if (GetDataBlock( gd, buf ) != 11)
break;
if (strncmp((char*)buf, "NETSCAPE2.0", 11))
break;
if (GetDataBlock( gd, buf ) != 3)
break;
if (buf[0] != 1)
break;
gd->loop = (unsigned int)buf[1] | (((unsigned int)buf[2]) << 8);
break;
case 0xfe: /* Comment Extension */
while ( GetDataBlock(gd,buf) != 0 )
;
return FALSE;
case 0xf9: /* Graphic Control Extension */
(void)GetDataBlock( gd, buf );
gd->g89.disposal = (buf[0] >> 2) & 0x7;
gd->g89.inputFlag = (buf[0] >> 1) & 0x1;
gd->g89.delayTime = LM_to_uint(buf[1],buf[2]);
if ( (buf[0] & 0x1) != 0 )
gd->g89.transparent = buf[3];
while ( GetDataBlock(gd,buf) != 0 )
;
return FALSE;
}
while ( GetDataBlock(gd,buf) != 0 )
;
return FALSE;
}
static int GetDataBlock( gifdata* gd, unsigned char* buf )
{
unsigned char count;
if ( !SDL_RWread(gd->src,&count,1,1) )
{
/* pm_message("error in getting DataBlock size" ); */
return -1;
}
gd->zerodatablock = count == 0;
if ( (count != 0) && !SDL_RWread(gd->src,buf,count,1) )
{
/* pm_message("error in reading DataBlock" ); */
return -1;
}
return count;
}
static int GetCode( gifdata* gd, int code_size, int flag )
{
int i, j, ret;
int count;
if ( flag )
{
gd->curbit = 0;
gd->lastbit = 0;
gd->done = FALSE;
return 0;
}
if ( (gd->curbit + code_size) >= gd->lastbit )
{
if ( gd->done )
{
if ( gd->curbit >= gd->lastbit )
SDL_SetError( "ran off the end of my bits" );
return -1;
}
gd->buf[0] = gd->buf[gd->lastbyte - 2];
gd->buf[1] = gd->buf[gd->lastbyte - 1];
if ( (count = GetDataBlock(gd, &gd->buf[2])) == 0 )
gd->done = TRUE;
gd->lastbyte = 2 + count;
gd->curbit = (gd->curbit - gd->lastbit) + 16;
gd->lastbit = (2 + count)*8;
}
ret = 0;
for ( i = gd->curbit, j = 0; j < code_size; ++i, ++j )
ret |= ((gd->buf[i / 8] & (1 << (i % 8))) != 0) << j;
gd->curbit += code_size;
return ret;
}
static int LWZReadByte( gifdata* gd, int flag, int input_code_size )
{
int i, code, incode;
if ( flag )
{
gd->setcodesize = input_code_size;
gd->codesize = gd->setcodesize + 1;
gd->clearcode = 1 << gd->setcodesize;
gd->endcode = gd->clearcode + 1;
gd->maxcodesize = gd->clearcode*2;
gd->maxcode = gd->clearcode + 2;
GetCode( gd, 0, TRUE );
gd->fresh = TRUE;
for ( i = 0; i < gd->clearcode; ++i )
{
gd->table[0][i] = 0;
gd->table[1][i] = i;
}
for ( ; i < (1 << MAX_LWZ_BITS); ++i )
gd->table[0][i] = gd->table[1][0] = 0;
gd->sp = gd->stack;
return 0;
}
else if ( gd->fresh )
{
gd->fresh = FALSE;
do
{
gd->firstcode = gd->oldcode = GetCode( gd, gd->codesize, FALSE );
} while ( gd->firstcode == gd->clearcode );
return gd->firstcode;
}
if ( gd->sp > gd->stack )
return *--gd->sp;
while ( (code = GetCode(gd,gd->codesize,FALSE)) >= 0 )
{
if ( code == gd->clearcode )
{
for ( i = 0; i < gd->clearcode; ++i )
{
gd->table[0][i] = 0;
gd->table[1][i] = i;
}
for ( ; i < (1 << MAX_LWZ_BITS); ++i )
gd->table[0][i] = gd->table[1][i] = 0;
gd->codesize = gd->setcodesize + 1;
gd->maxcodesize = gd->clearcode*2;
gd->maxcode = gd->clearcode + 2;
gd->sp = gd->stack;
gd->firstcode = gd->oldcode = GetCode( gd, gd->codesize, FALSE );
return gd->firstcode;
}
else if ( code == gd->endcode )
{
int count;
unsigned char buf[260];
if ( gd->zerodatablock )
return -2;
while ( (count = GetDataBlock(gd,buf)) > 0 )
;
if ( count != 0 )
{
/* pm_message("missing EOD in data stream (common occurence)"); */
}
return -2;
}
incode = code;
if ( code >= gd->maxcode )
{
*gd->sp++ = gd->firstcode;
code = gd->oldcode;
}
while ( code >= gd->clearcode )
{
*gd->sp++ = gd->table[1][code];
if ( code == gd->table[0][code] )
SDL_SetError( "circular table entry BIG ERROR" );
code = gd->table[0][code];
}
*gd->sp++ = gd->firstcode = gd->table[1][code];
if ( (code = gd->maxcode) < (1 << MAX_LWZ_BITS) )
{
gd->table[0][code] = gd->oldcode;
gd->table[1][code] = gd->firstcode;
++gd->maxcode;
if ( (gd->maxcode >= gd->maxcodesize) && (gd->maxcodesize < (1 << MAX_LWZ_BITS)) )
{
gd->maxcodesize *= 2;
++gd->codesize;
}
}
gd->oldcode = incode;
if ( gd->sp > gd->stack )
return *--gd->sp;
}
return code;
}
static SDL_Surface* ReadImage( gifdata* gd, int len, int height, int cmapSize, unsigned char cmap[3][MAXCOLORMAPSIZE], int interlace, int ignore )
{
SDL_Surface* image;
unsigned char c;
int i, v;
int xpos = 0, ypos = 0, pass = 0;
/* Initialize the compression routines */
if ( !SDL_RWread(gd->src,&c,1,1) )
{
SDL_SetError( "EOF / read error on image data" );
return NULL;
}
if ( LWZReadByte(gd,TRUE,c) < 0 )
{
SDL_SetError( "error reading image" );
return NULL;
}
/* If this is an "uninteresting picture" ignore it. */
if ( ignore )
{
while ( LWZReadByte(gd,FALSE,c) >= 0 )
;
return NULL;
}
image = SDL_AllocSurface( SDL_SWSURFACE, len, height, 8, 0, 0, 0, 0 );
for ( i = 0; i < cmapSize; i++ )
{
image->format->palette->colors[i].r = cmap[CM_RED][i];
image->format->palette->colors[i].g = cmap[CM_GREEN][i];
image->format->palette->colors[i].b = cmap[CM_BLUE][i];
}
while ( (v = LWZReadByte(gd,FALSE,c)) >= 0 )
{
((Uint8*)image->pixels)[xpos + ypos*image->pitch] = (Uint8)v;
++xpos;
if ( xpos == len )
{
xpos = 0;
if ( interlace )
{
switch ( pass )
{
case 0:
case 1: ypos += 8; break;
case 2: ypos += 4; break;
case 3: ypos += 2; break;
}
if ( ypos >= height )
{
++pass;
switch ( pass )
{
case 1: ypos = 4; break;
case 2: ypos = 2; break;
case 3: ypos = 1; break;
default: goto fini;
}
}
}
else
{
++ypos;
}
}
if ( ypos >= height )
break;
}
fini:
return image;
}