home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga MA Magazine 1998 #4
/
amigamamagazinepolishissue1998.iso
/
datatypes
/
gifanimdtc201.lha
/
gifanim_datatype
/
dispatch.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-03-23
|
92KB
|
2,491 lines
/*
**
** $VER: dispatch.c 2.1 (23.3.98)
** gifanim.datatype 2.1
**
** Dispatch routine for a DataTypes class
**
** Written 1997/1998 by Roland 'Gizzy' Mainz
** Original example source from David N. Junod
**
*/
/* main includes */
#include "classbase.h"
#include "classdata.h"
/* ansi includes */
#include <limits.h>
/*****************************************************************************/
/* debugging */
#if 0
void kprintf( STRPTR, ... );
#define D( x ) x
#else
#define D( x )
#endif
/*****************************************************************************/
/* decoder related local prototypes */
static BOOL ReadColorMap( struct ClassBase *, struct GIFAnimInstData *, UWORD, struct ColorRegister * );
static void DoExtension( struct ClassBase *, Object *, struct GIFAnimInstData *, TEXT );
static int GetDataBlock( struct ClassBase *, struct GIFAnimInstData *, UBYTE * );
static int GetCode( struct ClassBase *, struct GIFAnimInstData *, int, BOOL );
static int LWZReadByte( struct ClassBase *, struct GIFAnimInstData *, BOOL, int );
static void ReadImage( struct ClassBase *, struct GIFAnimInstData *, UBYTE *, UWORD, UWORD, UWORD, UWORD, UWORD, BOOL, BOOL, UWORD );
static void WriteDeltaPixelArray8Fast( struct BitMap *, UBYTE *, UBYTE * );
static int getbase2( int );
static BOOL ReadOK( struct ClassBase *, struct GIFDecoder *, void *, ULONG );
/*****************************************************************************/
/* local prototypes */
static BOOL ScanFrames( struct ClassBase *, Object * );
static struct FrameNode *AllocFrameNode( struct ClassBase *, APTR );
static struct FrameNode *FindFrameNode( struct MinList *, ULONG );
static void FreeFrameNodeResources( struct ClassBase *, struct GIFAnimInstData * );
static struct BitMap *AllocFrameBitMap( struct ClassBase *, struct GIFAnimInstData * );
static void FreeFrameBitMap( struct ClassBase *, struct GIFAnimInstData *, struct BitMap * );
static struct BitMap *AllocBitMapPooled( struct ClassBase *, ULONG, ULONG, ULONG, APTR );
static struct FrameNode *GetPrevFrameNode( struct FrameNode *, ULONG );
static void AttachSample( struct ClassBase *, struct GIFAnimInstData * );
/*****************************************************************************/
/* Create "gifanim.datatype" BOOPSI class */
struct IClass *initClass( struct ClassBase *cb )
{
struct IClass *cl;
/* Create our class... */
if( cl = MakeClass( GIFANIMDTCLASS, ANIMATIONDTCLASS, NULL, (ULONG)sizeof( struct GIFAnimInstData ), 0UL ) )
{
#define DTSTACKSIZE (16384UL)
cl -> cl_Dispatcher . h_Entry = (HOOKFUNC)StackSwapDispatch; /* see stackswap.c */
cl -> cl_Dispatcher . h_SubEntry = (HOOKFUNC)Dispatch; /* see stackswap.c */
cl -> cl_Dispatcher . h_Data = (APTR)DTSTACKSIZE; /* see stackswap.c */
cl -> cl_UserData = (ULONG)cb;
AddClass( cl );
}
return( cl );
}
/* class dispatcher */
DISPATCHERFLAGS
ULONG Dispatch( REGA0 struct IClass *cl, REGA2 Object *o, REGA1 Msg msg )
{
struct ClassBase *cb = (struct ClassBase *)(cl -> cl_UserData);
struct GIFAnimInstData *gaid;
ULONG retval = 0UL;
switch( msg -> MethodID )
{
/****** gifanim.datatype/OM_NEW **********************************************
*
* NAME
* OM_NEW -- Create a gifanim.datatype object.
*
* FUNCTION
* The OM_NEW method is used to create an instance of the
* gifanim.datatype class. This method is passed to the superclass
* first. After this, gifanim.datatype parses the prefs file and makes
* a scan through the data to get index information. Frame bitmaps are
* loaded if the input stream isn't seekable, colormaps and the first
* frame are loaded immediately.
* If a sample was set in the prefs, it will be loaded and attached
* to the animation.
*
* ATTRIBUTES
* The following attributes can be specified at creation time.
*
* DTA_SourceType (ULONG) -- Determinates the type of DTA_Handle
* attribute. Only DTST_FILE and DTST_RAM are supported.
* If any other type was set in a given DTA_SourceType,
* OM_NEW will be rejected.
* Defaults to DTST_FILE.
*
* DTA_Handle -- For DTST_FILE, a BPTR filehandle is expected. This
* handle will be created by datatypesclass depeding on the DTF_#?
* flag, which is DTF_BINARY here. DTST_FILE, datatypesclass
* creates a file handle from the given DTA_Name and DTA_Handle
* (a BPTR returned by Lock).
* A DTST_RAM (create empty object) source type requires a NULL
* handle.
*
* RESULT
* If the object was created a pointer to the object is returned,
* otherwise NULL is returned.
*
******************************************************************************
*
*/
case OM_NEW:
{
struct TagItem *ti;
/* We only support DTST_FILE or DTST_RAM as source type */
if( ti = FindTagItem( DTA_SourceType, (((struct opSet *)msg) -> ops_AttrList) ) )
{
if( ((ti -> ti_Data) != DTST_FILE) && ((ti -> ti_Data) != DTST_RAM) )
{
SetIoErr( ERROR_OBJECT_WRONG_TYPE );
break;
}
}
if( retval = DoSuperMethodA( cl, o, msg ) )
{
/* Load frames... */
if( !ScanFrames( cb, (Object *)retval ) )
{
/* Something went fatally wrong, dispose object */
CoerceMethod( cl, (Object *)retval, OM_DISPOSE );
retval = 0UL;
}
}
}
break;
/****** gifanim.datatype/OM_DISPOSE ******************************************
*
* NAME
* OM_DISPOSE -- Delete a gifanim.datatype object.
*
* FUNCTION
* The OM_DISPOSE method is used to delete an instance of the
* gifanim.datatype class. This method is passed to the superclass when
* it has completed.
* This method frees all frame nodes and their contents (bitmaps,
* colormaps, samples etc.)
*
* RESULT
* The object is deleted. 0UL is returned.
*
******************************************************************************
*
*/
case OM_DISPOSE:
{
LONG saved_ioerr = IoErr();
/* Get a pointer to our object data */
gaid = (struct GIFAnimInstData *)INST_DATA( cl, o );
/* Wait for any outstanding blitter usage (which may use one of our bitmaps) */
WaitBlit();
/* Free colormaps etc. (e.g. all resources which are NOT free'ed on DeletePool below) */
FreeFrameNodeResources( cb, gaid );
/* Free our key bitmap */
FreeBitMap( (gaid -> gaid_KeyBitMap) );
/* Delete the frame pool */
DeletePool( (gaid -> gaid_Pool) );
/* Close input file */
if( gaid -> gaid_FH )
{
Close( (gaid -> gaid_FH) );
}
/* Close verbose output file */
if( gaid -> gaid_VerboseOutput )
{
Close( (gaid -> gaid_VerboseOutput) );
}
/* Dispose object */
DoSuperMethodA( cl, o, msg );
/* Restore Result2 */
SetIoErr( saved_ioerr );
}
break;
/* TEST TEST / Support for format change "on-the-fly" disabled here / TEST TEST */
#ifdef COMMENTED_OUT
case DTM_FRAMEBOX:
{
struct dtFrameBox *dtf = (struct dtFrameBox *)msg;
gaid = (struct GIFAnimInstData *)INST_DATA( cl, o );
/* pass to superclas first */
retval = DoSuperMethodA( cl, o, msg );
/* Does someone tell me in what for an environment (screen) I'll be attached to ? */
if( (dtf -> dtf_FrameFlags) & FRAMEF_SPECIFY )
{
if( dtf -> dtf_ContentsInfo )
{
if( dtf -> dtf_ContentsInfo -> fri_Screen )
{
struct BitMap *bm = dtf -> dtf_ContentsInfo -> fri_Screen -> RastPort . BitMap;
/* Does we have a non-planar bitmap ? */
if( !(GetBitMapAttr( bm, BMA_FLAGS ) & BMF_STANDARD) )
{
/* I assume here that the system is able to map a 24 bit bitmap into the screen
* if it is deeper than 8 bit.
*/
if( ((bm -> Depth) > 8UL) && ((dtf -> dtf_ContentsInfo -> fri_Dimensions . Depth) > 8UL) )
{
verbose_printf( cb, gaid, "using chunky bitmap\n" );
}
}
}
}
}
}
break;
#endif /* COMMENTED_OUT */
case OM_UPDATE:
{
if( DoMethod( o, ICM_CHECKLOOP ) )
{
break;
}
}
case OM_SET:
{
/* Pass the attributes to the animation class and force a refresh if we need it */
if( retval = DoSuperMethodA( cl, o, msg ) )
{
/* Top instance ? */
if( OCLASS( o ) == cl )
{
struct RastPort *rp;
/* Get a pointer to the rastport */
if( rp = ObtainGIRPort( (((struct opSet *)msg) -> ops_GInfo) ) )
{
struct gpRender gpr;
/* Force a redraw */
gpr . MethodID = GM_RENDER;
gpr . gpr_GInfo = ((struct opSet *)msg) -> ops_GInfo;
gpr . gpr_RPort = rp;
gpr . gpr_Redraw = GREDRAW_UPDATE;
DoMethodA( o, (Msg)(&gpr) );
/* Release the temporary rastport */
ReleaseGIRPort( rp );
/* We did a refresh... */
retval = 0UL;
}
}
}
}
break;
/****** gifanim.datatype/DTM_WRITE *******************************************
*
* NAME
* DTM_WRITE -- Save data
*
* FUNCTION
* This method saves the object's contents to disk.
*
* If dtw_Mode is DTWM_IFF, the method is passed unchanged to the
* superclass, animation.datatype, which writes a single IFF ILBM
* picture.
*
* If dtw_mode is DTWM_RAW, the object saved an GIF Animation stream
* to the filehandle given, starting with the current frame until
* the end is reached.
* The sequence saved can be controlled by the ADTA_Frame, ADTA_Frames
* and ADTA_FrameIncrement attributes (see TAGS section below).
*
* TAGS
* When writing the local ("raw") format, GIF Animation, the following
* attributes are recognized:
*
* ADTA_Frame (ULONG) - start frame, saving starts here.
* Defaults to the current frame displayed.
*
* ADTA_Frames (ULONG) - the number of frames to be saved,
* Defaults to (max_num_of_frames - curr_frame).
*
* ADTA_FrameIncrement (ULONG) - frame increment when saving.
* Defaults to 1, which means: "jump to next frame".
*
* NOTE
* - Any sound attached to the animation will NOT be saved.
*
* - A CTRL-D signal to the writing process aborts the save.
*
* RESULT
* Returns 0 for failure (IoErr() returns result2), non-zero
* for success.
*
******************************************************************************
*
*/
case DTM_WRITE:
{
struct dtWrite *dtw;
dtw = (struct dtWrite *)msg;
/* Local data format requested ?... */
if( (dtw -> dtw_Mode) == DTWM_RAW )
{
retval = SaveGIFAnim( cb, cl, o, dtw );
}
else
{
/* Pass msg to superclass (which writes a single frame as an IFF ILBM picture)... */
retval = DoSuperMethodA( cl, o, msg );
}
}
break;
/****** gifanim.datatype/ADTM_LOADFRAME *****************************************
*
* NAME
* ADTM_LOADFRAME -- Load frame
*
* FUNCTION
* The ADTM_LOADFRAME method is used to obtain the bitmap and timing
* data of the animation.
* The given timestamp will be used to find a matching timestamp
* in the internal FrameNode list. If it was found, the corresponding
* timing, bitmap and colormap data are stored into the struct
* adtFrame. If the bitmap wasn't loaded, this method attempts to
* load it from disk.
*
* RESULT
* the bitmap ptr if a bitmap was found,
* 0 (and result2 with the reason).
*
******************************************************************************
*
*/
case ADTM_LOADFRAME:
{
struct adtFrame *alf = (struct adtFrame *)msg;
struct adtFrame freeframe;
struct FrameNode *fn;
LONG error = 0L;
gaid = (struct GIFAnimInstData *)INST_DATA( cl, o );
ObtainSemaphore( (&(gaid -> gaid_SigSem)) );
/* Like "realloc": Free any given frame (the free is done AFTER the load to
* avoid that a frame which is loaded will be freed and then loaded again...
*/
if( alf -> alf_UserData )
{
/* Copy message contents that we can call ADTM_UNLOADFRAME below */
freeframe = *alf;
alf -> alf_UserData = NULL; /* "freeframe" now owns the frame data to free ! */
}
else
{
/* No data to free... */
freeframe . alf_UserData = NULL;
}
/* Find frame by timestamp */
if( fn = FindFrameNode( (&(gaid -> gaid_FrameList)), (alf -> alf_TimeStamp) ) )
{
/* Load bitmaps only if we don't cache the whole anim and
* if we have a filehandle to load from (an empty object created using DTST_RAM won't have this)...
*/
if( (!(gaid -> gaid_LoadAll) == FALSE) && (gaid -> gaid_FH) )
{
/* If no bitmap is loaded, load it... */
if( (fn -> fn_BitMap) == NULL )
{
ULONG animwidth = (ULONG)(gaid -> gaid_PaddedWidth),
animheight = (ULONG)(gaid -> gaid_Height);
/* Allocate array for chunkypixel data */
if( fn -> fn_ChunkyMap = (UBYTE *)AllocVecPooled( cb, (gaid -> gaid_Pool), ((animwidth * animheight) + 256) ) )
{
/* Get a clean background to avoid that rubbish shows througth transparent parts */
memset( (fn -> fn_ChunkyMap), 0, (size_t)(animwidth * animheight) );
if( fn -> fn_BitMap = AllocFrameBitMap( cb, gaid ) )
{
struct FrameNode *worknode = fn;
struct FrameNode *prevnode = NULL;
ULONG rollback = 0UL;
UBYTE *deltamap = NULL;
struct GIFDecoder *gifdec = (&(gaid -> gaid_GIFDec));
/* See if we need a rollback (if TRUE, we copy (below) the previous chunkymap into our
* current chunkymap as background. If Left/Top != 0 or transparent colors are present,
* parts of this previous image will occur).
*/
switch( fn -> fn_GIF89aDisposal )
{
case GIF89A_DISPOSE_NODISPOSE:
case GIF89A_DISPOSE_RESTOREPREVIOUS:
{
do
{
worknode = GetPrevFrameNode( worknode, 1UL );
rollback++;
} while( ((worknode -> fn_ChunkyMap) == NULL) && ((worknode -> fn_TimeStamp) != 0UL) );
}
break;
}
if( ((worknode -> fn_ChunkyMap) == NULL) && ((worknode -> fn_TimeStamp) == 0UL) )
{
error_printf( cb, gaid, "first frame without bitmap ... !\n" );
}
do
{
ULONG current = rollback;
worknode = fn;
while( current-- )
{
worknode = GetPrevFrameNode( worknode, 1UL );
}
if( (worknode -> fn_ChunkyMap) && (worknode != fn) )
{
prevnode = worknode;
}
else
{
if( Seek( (gaid -> gaid_FH), ((worknode -> fn_BMOffset) - (gaid -> gaid_CurrFilePos)), OFFSET_CURRENT ) != (-1L) )
{
if( gifdec -> file_buffer = AllocVec( ((worknode -> fn_BMSize) + 16UL), MEMF_PUBLIC ) )
{
BOOL useGlobalColormap;
UWORD bitPixel;
UBYTE buf[ 16 ];
/* Init buffer */
gifdec -> buffer = gifdec -> file_buffer;
gifdec -> buffersize = worknode -> fn_BMSize;
gifdec -> which_fh = WHICHFH_BUFFER;
/* Fill buffer */
if( Read( (gifdec -> file), (gifdec -> buffer), (gifdec -> buffersize) ) == (gifdec -> buffersize) )
{
if( !ReadOK( cb, gifdec, buf, 9 ) )
{
error = IoErr();
D( kprintf( "couldn't read left/top/width/height\n" ) );
}
useGlobalColormap = !BitSet( buf[ 8 ], LOCALCOLORMAP );
bitPixel = 1 << ((buf[ 8 ] & 0x07) + 1);
/* disposal method */
switch( worknode -> fn_GIF89aDisposal )
{
case GIF89A_DISPOSE_NOP:
{
/* Background not transparent ? */
if( ((worknode -> fn_GIF89aTransparent) == ~0U) ||
((worknode -> fn_GIF89aTransparent) != 0U) )
{
/* Restore to color 0 */
memset( (fn -> fn_ChunkyMap), 0, (size_t)(animwidth * animheight) );
}
}
break;
case GIF89A_DISPOSE_NODISPOSE:
{
/* do not dispose prev image */
/* Check if we have a prevnode link to the previous image.
* If this is NULL, we assume that our chunkymap already contain
* the previous image
*/
if( prevnode )
{
CopyMem( (prevnode -> fn_ChunkyMap), (fn -> fn_ChunkyMap), (animwidth * animheight) );
#ifdef DELTAWPA8
CopyBitMap( cb, (fn -> fn_BitMap), (prevnode -> fn_BitMap), animwidth, animheight );
deltamap = prevnode -> fn_ChunkyMap;
#endif /* DELTAWPA8 */
}
else
{
#ifdef DELTAWPA8
deltamap = NULL;
#endif /* DELTAWPA8 */
}
}
break;
case GIF89A_DISPOSE_RESTOREBACKGROUND:
{
/* Background not transparent ? */
if( ((worknode -> fn_GIF89aTransparent) == ~0U) ||
((worknode -> fn_GIF89aTransparent) != (gaid -> gaid_GIFDec . GifScreen . Background)) )
{
/* Restore to background color */
memset( (fn -> fn_ChunkyMap), (gaid -> gaid_GIFDec . GifScreen . Background), (size_t)(animwidth * animheight) );
}
}
break;
case GIF89A_DISPOSE_RESTOREPREVIOUS:
{
/* restore image of previous frame */
/* Check if we have a prevnode link to the previous image.
* If this is NULL, we assume that our chunkymap already contain
* the previous image
*/
if( prevnode )
{
CopyMem( (prevnode -> fn_ChunkyMap), (fn -> fn_ChunkyMap), (animwidth * animheight) );
#ifdef DELTAWPA8
CopyBitMap( cb, (fn -> fn_BitMap), (prevnode -> fn_BitMap), animwidth, animheight );
deltamap = prevnode -> fn_ChunkyMap;
#endif /* DELTAWPA8 */
}
else
{
#ifdef DELTAWPA8
deltamap = NULL;
#endif /* DELTAWPA8 */
}
}
break;
default: /* GIF89A_DISPOSE_RESERVED4 - GIF89A_DISPOSE_RESERVED7 */
{
error_printf( cb, gaid, "unsupported disposal method %lu\n", (ULONG)(gaid -> gaid_GIFDec . Gif89 . disposal) );
}
break;
}
if( !useGlobalColormap )
{
/* Skip colormap (in buffer) */
gifdec -> buffer += (sizeof( struct ColorRegister ) * bitPixel);
}
ReadImage( cb, gaid,
(fn -> fn_ChunkyMap),
(UWORD)animwidth,
LOHI2UINT16( buf[ 0 ], buf[ 1 ] ),
LOHI2UINT16( buf[ 2 ], buf[ 3 ] ),
LOHI2UINT16( buf[ 4 ], buf[ 5 ] ),
LOHI2UINT16( buf[ 6 ], buf[ 7 ] ),
BitSet( buf[ 8 ], INTERLACE ),
FALSE,
(worknode -> fn_GIF89aTransparent) );
}
FreeVec( (gifdec -> file_buffer) );
gifdec -> file_buffer = gifdec -> buffer = NULL;
}
/* Bump file pos */
gaid -> gaid_CurrFilePos = Seek( (gaid -> gaid_FH), 0L, OFFSET_CURRENT ); /* BUG: does not check for failure */
}
else
{
/* seek failed */
error = IoErr();
break;
}
prevnode = NULL; /* a previous image is now in our chunkymap,
* we don't need the link anymore
*/
}
} while( rollback-- );
if( error == 0L )
{
if( fn -> fn_ChunkyMap )
{
if( gaid -> gaid_UseChunkyMap )
{
WriteRGBPixelArray8( cb, (fn -> fn_BitMap), animwidth, animheight, (fn -> fn_ColorMap), (fn -> fn_ChunkyMap) );
}
else
{
WriteDeltaPixelArray8Fast( (fn -> fn_BitMap), (fn -> fn_ChunkyMap), deltamap );
}
}
}
}
else
{
/* can't alloc bitmap */
error = ERROR_NO_FREE_STORE;
}
}
else
{
/* can't alloc chunkymap */
error = ERROR_NO_FREE_STORE;
}
}
}
/* Store timing/context information */
alf -> alf_Duration = fn -> fn_Duration;
alf -> alf_Frame = fn -> fn_Frame;
alf -> alf_UserData = (APTR)fn; /* Links back to this FrameNode (used by ADTM_UNLOADFRAME) */
/* Store bitmap information */
alf -> alf_BitMap = fn -> fn_BitMap;
alf -> alf_CMap = ((gaid -> gaid_UseChunkyMap)?(NULL):(fn -> fn_CMap)); /* we does not use a colormap with a direct RGB-coded bitmap */
/* Is there a sample to play ? */
if( fn -> fn_Sample )
{
/* Store sound information */
alf -> alf_Sample = fn -> fn_Sample;
alf -> alf_SampleLength = fn -> fn_SampleLength;
alf -> alf_Period = fn -> fn_Period;
}
else
{
/* No sound */
alf -> alf_Sample = NULL;
alf -> alf_SampleLength = 0UL;
alf -> alf_Period = 0UL;
}
/* Frame "in use", even for a unsuccessful result; on error
* animation.datatype send an ADTM_UNLOADFRAME which frees
* allocated resources and decreases the "UseCount"...
*/
fn -> fn_UseCount++;
/* Return bitmap ptr of possible, 0UL and error cause otherwise */
retval = ((error)?(0UL):(ULONG)(alf -> alf_BitMap)); /* Result */
}
else
{
/* no matching frame found */
error = ERROR_OBJECT_NOT_FOUND;
}
/* Like "realloc": Free any given frame here */
if( freeframe . alf_UserData )
{
freeframe . MethodID = ADTM_UNLOADFRAME;
DoMethodA( o, (Msg)(&freeframe) );
}
SetIoErr( error ); /* Result2 */
ReleaseSemaphore( (&(gaid -> gaid_SigSem)) );
}
break;
/****** gifanim.datatype/ADTM_UNLOADFRAME ************************************
*
* NAME
* ADTM_UNLOADFRAME -- Unload frame contents
*
* FUNCTION
* The ADTM_UNLOADFRAME method is used to release the contents of a
* animation frame.
*
* This method frees the bitmap data found in adtFrame.
*
* RESULT
* Returns always 0UL.
*
******************************************************************************
*
*/
case ADTM_UNLOADFRAME:
{
struct FrameNode *fn;
struct adtFrame *alf;
gaid = (struct GIFAnimInstData *)INST_DATA( cl, o );
alf = (struct adtFrame *)msg;
/* Free bitmaps only if we don't cache the whole anim */
if( (gaid -> gaid_LoadAll) == FALSE )
{
ObtainSemaphore( (&(gaid -> gaid_SigSem)) );
if( fn = (struct FrameNode *)(alf -> alf_UserData) )
{
if( (fn -> fn_UseCount) > 0 )
{
fn -> fn_UseCount--;
/* Free an existing bitmap if it isn't in use and if it is NOT the first bitmap */
if( ((fn -> fn_UseCount) == 0) && (fn -> fn_BitMap) && (fn != (struct FrameNode *)(gaid -> gaid_FrameList . mlh_Head)) )
{
FreeFrameBitMap( cb, gaid, (fn -> fn_BitMap) );
FreeVecPooled( cb, (gaid -> gaid_Pool), (fn -> fn_ChunkyMap) );
fn -> fn_BitMap = NULL;
fn -> fn_ChunkyMap = NULL;
}
}
}
ReleaseSemaphore( (&(gaid -> gaid_SigSem)) );
}
/* The frame has been freed ! */
alf -> alf_UserData = NULL;
}
break;
/* Let the superclass handle everything else */
default:
{
retval = DoSuperMethodA( cl, o, msg );
}
break;
}
return( retval );
}
static
BOOL ScanFrames( struct ClassBase *cb, Object *o )
{
struct GIFAnimInstData *gaid = (struct GIFAnimInstData *)INST_DATA( (cb -> cb_Lib . cl_Class), o );
LONG error = 0L;
BOOL success = FALSE;
InitSemaphore( (&(gaid -> gaid_SigSem)) );
NewList( (struct List *)(&(gaid -> gaid_FrameList)) );
/* Create a memory pool for frame nodes */
if( gaid -> gaid_Pool = CreatePool( MEMF_PUBLIC, 8192UL, 8192UL ) )
{
BPTR fh; /* handle (file handle) */
ULONG sourcetype; /* type of stream (either DTST_FILE or DTST_RAM) */
ULONG modeid /*= (ULONG)INVALID_ID*/; /* anim view mode */
ULONG animwidth, /* anim width */
animheight, /* anim height */
animdepth; /* anim depth */
ULONG timestamp = 0UL; /* timestamp */
/* Prefs defaults */
gaid -> gaid_LoadAll = TRUE; /* The decoder is too slow to allow realtime decoding of a
* 576 * 124 * 8 GIF Image, even on a mc68040 :-((
*/
gaid -> gaid_ModeID = (ULONG)INVALID_ID; /* no screen mode selected yet */
gaid -> gaid_Volume = 64UL;
/* Read prefs */
ReadENVPrefs( cb, gaid );
/* Get file handle, handle type and BitMapHeader */
if( GetDTAttrs( o, DTA_SourceType, (&sourcetype),
DTA_Handle, (&fh),
DTA_Name, (&(gaid -> gaid_ProjectName)),
TAG_DONE ) == 3UL )
{
switch( sourcetype )
{
case DTST_FILE:
{
if( fh )
{
BPTR lock;
if( lock = DupLockFromFH( fh ) )
{
/* Set up a filehandle for disk-based loading (random loading) */
if( gaid -> gaid_FH = (LONG)OpenFromLock( lock ) )
{
success = TRUE;
}
else
{
/* failure */
UnLock( lock );
}
}
}
/* OpenFromLock failed ? - Then open by name :-( */
if( (gaid -> gaid_FH) == NULL )
{
/* Set up a filehandle for disk-based loading (random loading) */
if( gaid -> gaid_FH = (LONG)Open( (gaid -> gaid_ProjectName), MODE_OLDFILE ) )
{
success = TRUE;
}
else
{
/* Can't open file */
error = IoErr();
}
}
}
break;
case DTST_RAM: /* empty object */
{
success = TRUE;
}
break;
default:
{
/* unsupported source type */
error = ERROR_NOT_IMPLEMENTED;
}
break;
}
/* Any error ? */
if( success )
{
/* Now we enter the next stage of testing... */
success = FALSE;
if( fh )
{
struct GIFDecoder *gifdec = (&(gaid -> gaid_GIFDec));
struct FrameNode *fn;
ULONG numcmaps = 0UL; /* number of created cmaps */
UBYTE buf[ 16 ];
struct ColorRegister localColorMap[ MAXCOLORMAPSIZE ] = { 0 };
struct ColorRegister savedTransparentColor = { 0 };
UBYTE c;
BOOL useGlobalColormap;
UWORD bitPixel;
UBYTE version[ 4 ];
gifdec -> file = fh;
gifdec -> Gif89 . transparent = (UWORD)~0U; /* means: no transparent color */
/* Read "GIF" indentifer and version */
if( ReadOK( cb, gifdec, buf, 6 ) )
{
/* Is there the GIF signature ? */
if( !strncmp( (char *)buf, "GIF", 3 ) )
{
strncpy( version, (char *)(buf + 3), 3 );
version[ 3 ] = '\0';
/* Check if we support this GIF version */
if( (!strcmp( version, "87a" )) ||
(!strcmp( version, "89a" )) )
{
/* Read GIF Screen */
if( ReadOK( cb, gifdec, buf, 7 ) )
{
struct FrameNode *prevnode = NULL;
UBYTE *deltamap = NULL;
gifdec -> GifScreen . Width = LOHI2UINT16( buf[ 0 ], buf[ 1 ] );
gifdec -> GifScreen . Height = LOHI2UINT16( buf[ 2 ], buf[ 3 ] );
gifdec -> GifScreen . BitPixel = 2 << (buf[ 4 ] & 0x07);
gifdec -> GifScreen . ColorResolution = (((buf[ 4 ] & 0x70) >> 3) + 1);
gifdec -> GifScreen . Background = buf[ 5 ];
gifdec -> GifScreen . AspectRatio = buf[ 6 ];
gaid -> gaid_Width = gifdec -> GifScreen . Width;
animwidth = gaid -> gaid_PaddedWidth = (gaid -> gaid_UseChunkyMap)?(gaid -> gaid_Width):(((gaid -> gaid_Width) + 15UL) & ~15UL); /* align for c2p-wpa (c2c does not need padding) */
animheight = gaid -> gaid_Height = gifdec -> GifScreen . Height;
animdepth = gaid -> gaid_Depth = (gaid -> gaid_UseChunkyMap)?(DIRECTRGB_DEPTH):((ULONG)getbase2( (gifdec -> GifScreen . BitPixel) ));
/* Global Colormap ? */
if( BitSet( buf[ 4 ], LOCALCOLORMAP ) )
{
numcmaps++;
if( ReadColorMap( cb, gaid, (gifdec -> GifScreen . BitPixel), (gifdec -> GifScreen . ColorMap) ) )
{
error = IoErr();
D( kprintf( "error reading global colormap\n" ) );
}
}
else
{
/* No global colormap ? - Then the background color in the GifScreen is a NOP */
gifdec -> GifScreen . Background = 0U;
}
for( ;; )
{
/* Read chunk ID char */
if( !ReadOK( cb, gifdec, (&c), 1 ) )
{
error = IoErr();
D( kprintf( "EOF / read error on image data\n" ) );
}
switch( c )
{
case ';': /* GIF terminator ? */
{
goto scandone;
}
case '!': /* Extension ? */
{
if( !ReadOK( cb, gifdec, (&c), 1 ) )
{
error = IoErr();
D( kprintf( "OF / read error on extension function code\n" ) );
}
DoExtension( cb, o, gaid, c );
}
break;
case ',': /* Raster data start ? */
{
/* Create an prepare a new frame node */
if( fn = AllocFrameNode( cb, (gaid -> gaid_Pool) ) )
{
if( (gaid -> gaid_LoadAll) || (timestamp == 0UL) )
{
if( !(fn -> fn_BitMap = AllocFrameBitMap( cb, gaid ) ) )
{
error = ERROR_NO_FREE_STORE;
}
/* Allocate array for chunkypixel data */
if( fn -> fn_ChunkyMap = (UBYTE *)AllocVecPooled( cb, (gaid -> gaid_Pool), ((animwidth * animheight) + 256) ) )
{
/* Get a clean background to avoid that rubbish shows througth transparent parts */
memset( (fn -> fn_ChunkyMap), 0, (size_t)(animwidth * animheight) );
}
else
{
error = ERROR_NO_FREE_STORE;
}
}
if( error == 0L )
{
ULONG duration;
/* Get position of bitmap */
fn -> fn_BMOffset = Seek( fh, 0L, OFFSET_CURRENT ); /* BUG: does not check for failure */
D( kprintf( "pos %lu\n", (fn -> fn_BMOffset) ) );
if( !ReadOK( cb, gifdec, buf, 9 ) )
{
error = IoErr();
D( kprintf( "couldn't read left/top/width/height\n" ) );
}
/* Local color map ? */
useGlobalColormap = !BitSet( buf[ 8 ], LOCALCOLORMAP );
/* Size of local color map */
bitPixel = 1 << ((buf[ 8 ] & 0x07) + 1);
/* Store GIF89a related attributes */
fn -> fn_GIF89aDisposal = gifdec -> Gif89 . disposal; /* Store disposal mode for current frame */
fn -> fn_GIF89aTransparent = gifdec -> Gif89 . transparent; /* Store currents frame transparent color */
if( fn -> fn_ChunkyMap )
{
/* disposal method */
switch( fn -> fn_GIF89aDisposal )
{
case GIF89A_DISPOSE_NOP:
{
/* Background not transparent ? */
if( ((fn -> fn_GIF89aTransparent) == ~0U) ||
((fn -> fn_GIF89aTransparent) != 0U) )
{
/* restore to color 0 */
memset( (fn -> fn_ChunkyMap), 0, (size_t)(animwidth * animheight) );
}
}
break;
case GIF89A_DISPOSE_NODISPOSE:
{
/* do not dispose prev image */
/* If we have a previous frame, copy it */
if( prevnode )
{
CopyMem( (prevnode -> fn_ChunkyMap), (fn -> fn_ChunkyMap), (animwidth * animheight) );
#ifdef DELTAWPA8
CopyBitMap( cb, (fn -> fn_BitMap), (prevnode -> fn_BitMap), animwidth, animheight );
deltamap = prevnode -> fn_ChunkyMap;
#endif /* DELTAWPA8 */
}
else
{
/* Background not transparent ? */
if( ((fn -> fn_GIF89aTransparent) == ~0U) ||
((fn -> fn_GIF89aTransparent) != 0U) )
{
/* restore to color 0 */
memset( (fn -> fn_ChunkyMap), 0, (size_t)(animwidth * animheight) );
}
#ifdef DELTAWPA8
deltamap = NULL;
#endif /* DELTAWPA8 */
}
}
break;
case GIF89A_DISPOSE_RESTOREBACKGROUND:
{
/* Background not transparent ? */
if( ((fn -> fn_GIF89aTransparent) == ~0U) ||
((fn -> fn_GIF89aTransparent) != (gaid -> gaid_GIFDec . GifScreen . Background)) )
{
/* Restore to background color */
memset( (fn -> fn_ChunkyMap), (gifdec -> GifScreen . Background), (size_t)(animwidth * animheight) );
}
}
break;
case GIF89A_DISPOSE_RESTOREPREVIOUS:
{
/* restore previous image */
/* If we have a previous frame, copy it */
if( prevnode )
{
CopyMem( (prevnode -> fn_ChunkyMap), (fn -> fn_ChunkyMap), (animwidth * animheight) );
#ifdef DELTAWPA8
CopyBitMap( cb, (fn -> fn_BitMap), (prevnode -> fn_BitMap), animwidth, animheight );
deltamap = prevnode -> fn_ChunkyMap;
#endif /* DELTAWPA8 */
}
else
{
/* restore to color 0 */
memset( (fn -> fn_ChunkyMap), 0, (size_t)(animwidth * animheight) );
#ifdef DELTAWPA8
deltamap = NULL;
#endif /* DELTAWPA8 */
}
}
break;
default: /* GIF89A_DISPOSE_RESERVED4 - GIF89A_DISPOSE_RESERVED7 */
{
error_printf( cb, gaid, "unsupported disposal method %lu\n", (ULONG)(gifdec -> Gif89 . disposal) );
}
break;
}
}
/* Save transparent color (if we have one) */
if( ((fn -> fn_GIF89aTransparent) != ~0U) && (timestamp != 0UL) )
{
savedTransparentColor = localColorMap[ (fn -> fn_GIF89aTransparent) ];
}
/* Get colormap */
if( useGlobalColormap )
{
/* use global colormap and depth */
bitPixel = gifdec -> GifScreen . BitPixel;
memcpy( localColorMap, (gifdec -> GifScreen . ColorMap), (sizeof( struct ColorRegister ) * bitPixel) );
}
else
{
numcmaps++;
if( ReadColorMap( cb, gaid, bitPixel, localColorMap ) )
{
error_printf( cb, gaid, "error reading local colormap\n" );
error = IoErr();
}
}
/* Restore transparent color (if we have one) */
if( (fn -> fn_GIF89aTransparent) != ~0U )
{
localColorMap[ (fn -> fn_GIF89aTransparent) ] = savedTransparentColor;
}
if( !(gaid -> gaid_UseChunkyMap) )
{
/* The first palette must be moved to the object's palette */
if( timestamp == 0UL )
{
if( !CMAP2Object( cb, o, (UBYTE *)localColorMap, (ULONG)(bitPixel * 3UL) ) )
{
/* can't alloc object's color table */
error = ERROR_NO_FREE_STORE;
}
}
/* Create a palette-per-frame colormap here */
if( !(fn -> fn_CMap = CMAP2ColorMap( cb, (1UL << (ULONG)(gaid -> gaid_Depth)), (UBYTE *)localColorMap, (ULONG)(bitPixel * 3UL) )) )
{
/* can't alloc colormap */
error = ERROR_NO_FREE_STORE;
}
}
/* Copy colormap for 24 bit output */
memcpy( (void *)(fn -> fn_ColorMap), (void *)localColorMap, (size_t)(sizeof( struct ColorRegister ) * bitPixel) );
ReadImage( cb, gaid,
(fn -> fn_ChunkyMap),
(UWORD)animwidth,
LOHI2UINT16( buf[ 0 ], buf[ 1 ] ),
LOHI2UINT16( buf[ 2 ], buf[ 3 ] ),
LOHI2UINT16( buf[ 4 ], buf[ 5 ] ),
LOHI2UINT16( buf[ 6 ], buf[ 7 ] ),
BitSet( buf[ 8 ], INTERLACE ),
((fn -> fn_BitMap) == NULL),
(fn -> fn_GIF89aTransparent) );
/* Get size of bitmap (curr_pos - start_of_bm) */
fn -> fn_BMSize = Seek( fh, 0L, OFFSET_CURRENT ) - (fn -> fn_BMOffset); /* BUG: does not check for failure */
if( fn -> fn_BitMap )
{
if( gaid -> gaid_UseChunkyMap )
{
WriteRGBPixelArray8( cb, (fn -> fn_BitMap), animwidth, animheight, (fn -> fn_ColorMap), (fn -> fn_ChunkyMap) );
}
else
{
WriteDeltaPixelArray8Fast( (fn -> fn_BitMap), (fn -> fn_ChunkyMap), deltamap );
}
}
/* Bump timestamp... */
if( ((gifdec -> Gif89 . delayTime) != ~0U) &&
((gifdec -> Gif89 . delayTime) > 1U) &&
((gifdec -> Gif89 . delayTime) < 2000U) )
{
duration = (gifdec -> Gif89 . delayTime);
}
else
{
duration = 0UL;
}
fn -> fn_TimeStamp = timestamp;
fn -> fn_Frame = timestamp;
fn -> fn_Duration = duration;
AddTail( (struct List *)(&(gaid -> gaid_FrameList)), (struct Node *)(&(fn -> fn_Node)) );
prevnode = fn;
/* Next frame starts at timestamp... */
timestamp += (fn -> fn_Duration) + 1UL;
}
}
}
break;
default: /* Not a valid raster data start character ? */
{
error_printf( cb, gaid, "invalid character 0x%02x, ignoring\n", (int)c );
}
break;
}
/* on error break */
if( error )
{
break;
}
}
scandone:
/* Any frames ? */
if( timestamp && (error == 0L) && numcmaps )
{
if( numcmaps == 1UL )
{
/* We only have a global colormap and no colormap changes (or a direct RGB bitmap),
* delete first colormap (a colormap in the first frames indicates following colormap
* changes)
*/
struct FrameNode *firstnode = (struct FrameNode *)(gaid -> gaid_FrameList . mlh_Head);
if( firstnode -> fn_CMap )
{
FreeColorMap( (firstnode -> fn_CMap) );
firstnode -> fn_CMap = NULL;
}
}
else
{
/* All frames must have a colormap, therefore we replicate the colormap
* from the previous colormap if one is missing
*/
struct FrameNode *worknode,
*nextnode;
struct ColorMap *currcm = NULL;
verbose_printf( cb, gaid, "Animation has palette changes per frame\n" );
worknode = (struct FrameNode *)(gaid -> gaid_FrameList . mlh_Head);
while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
{
if( worknode -> fn_CMap )
{
/* Current node contains colormap, this are the colors for the following frames... */
currcm = worknode -> fn_CMap;
}
else
{
if( currcm )
{
/* Copy colormap from previous one... */
if( !(worknode -> fn_CMap = CopyColorMap( cb, currcm )) )
{
/* Can't copy/alloc colormap */
error = ERROR_NO_FREE_STORE;
}
}
else
{
verbose_printf( cb, gaid, "scan/load: no colormap, can't copy it\n" );
}
}
worknode = nextnode;
}
}
}
/* Check for required information */
if( error == 0L )
{
/* Any frames loaded ? */
if( timestamp == 0UL )
{
/* not enougth frames (at least one required) */
error = DTERROR_NOT_ENOUGH_DATA;
}
}
/* Any error ? */
if( error == 0L )
{
struct FrameNode *firstfn = (struct FrameNode *)(gaid -> gaid_FrameList . mlh_Head); /* short cut to the first FrameNode */
/* Alloc bitmap as key bitmap */
if( gaid -> gaid_UseChunkyMap )
{
gaid -> gaid_KeyBitMap = AllocBitMap( animwidth, animheight, animdepth, (BMF_SPECIALFMT | SHIFT_PIXFMT( DIRECTRGB_PIXFMT )), NULL );
}
else
{
gaid -> gaid_KeyBitMap = AllocBitMap( animwidth, animheight, animdepth, BMF_CLEAR, NULL );
}
if( gaid -> gaid_KeyBitMap )
{
if( (firstfn -> fn_BitMap) == NULL )
{
/* can't alloc first bitmap */
error = ERROR_NO_FREE_STORE;
}
if( error == 0L )
{
/* Copy first frame into key bitmap */
CopyBitMap( cb, (gaid -> gaid_KeyBitMap), (firstfn -> fn_BitMap), animwidth, animheight );
/* No screen mode id set by prefs ? */
if( (gaid -> gaid_ModeID) != (ULONG)INVALID_ID )
{
modeid = gaid -> gaid_ModeID;
}
else
{
if( gaid -> gaid_UseChunkyMap )
{
/* We don't have fixed values for cybergfx mode id's, therefore we have to ask for them */
if( (modeid = BestCModeIDTags( CYBRBIDTG_NominalWidth, animwidth,
CYBRBIDTG_NominalHeight, animheight,
CYBRBIDTG_Depth, animdepth,
TAG_DONE )) == INVALID_ID )
{
#if 0
error = 1L; /* inducate an error here :-( */
#else
/* Workaround for CyberGFX bug :-( */
if( (modeid = BestCModeIDTags( CYBRBIDTG_NominalWidth, 640UL,
CYBRBIDTG_NominalHeight, 480UL,
CYBRBIDTG_Depth, animdepth,
TAG_DONE )) == INVALID_ID )
{
modeid = 0UL;
error_printf( cb, gaid, "'CyberGFX bug' workaround failed, too\n" );
}
#endif
error_printf( cb, gaid, "No screenmode available for %lu/%lu/%lu\n", animwidth, animheight, animdepth );
}
}
else
{
/* BUG: Does currently not support SUPERHIRES modes */
if( animwidth >= 640UL )
{
if( animheight >= 400 )
{
modeid = HIRESLACE_KEY;
}
else
{
modeid = HIRES_KEY;
}
}
else
{
if( animheight >= 400 )
{
modeid = LORESLACE_KEY;
}
else
{
modeid = LORES_KEY;
}
}
}
}
/* No fps set by prefs ? */
if( (gaid -> gaid_FPS) == 0UL )
{
gaid -> gaid_FPS = 100; /* defaults to 100 fps. GIF 89a delay values counts in
* 1/100 sec steps. We set the alf_Duration field
* to this value (got from the GIF 89a extension).
*/
}
AttachSample( cb, gaid );
verbose_printf( cb, gaid, "width %lu height %lu depth %lu frames %lu fps %lu\n",
animwidth,
animheight,
animdepth,
timestamp,
(gaid -> gaid_FPS) );
/* Set misc attributes */
SetDTAttrs( o, NULL, NULL,
DTA_ObjName, (gaid -> gaid_ProjectName),
DTA_TotalHoriz, animwidth,
DTA_TotalVert, animheight,
DTA_Repeat, (gaid -> gaid_Repeat),
ADTA_Width, (gaid -> gaid_Width),
ADTA_Height, animheight,
ADTA_Depth, animdepth,
ADTA_Frames, timestamp,
ADTA_FramesPerSecond, (gaid -> gaid_FPS),
ADTA_ModeID, modeid,
ADTA_KeyFrame, (gaid -> gaid_KeyBitMap),
XTAG( (firstfn -> fn_Sample), ADTA_Sample ), (firstfn -> fn_Sample),
XTAG( (firstfn -> fn_Sample), ADTA_SampleLength ), ((firstfn -> fn_SampleLength) / ((firstfn -> fn_Duration) + 1UL)),
XTAG( (firstfn -> fn_Sample), ADTA_Period ), (firstfn -> fn_Period),
XTAG( (firstfn -> fn_Sample), ADTA_Volume ), (gaid -> gaid_Volume),
TAG_DONE );
/* All done for now... */
success = TRUE;
}
}
else
{
/* can't alloc key bitmap */
error = ERROR_NO_FREE_STORE;
}
}
}
else
{
error = IoErr();
D( kprintf( "failed to read screen descriptor\n" ) );
}
}
else
{
error = DTERROR_UNKNOWN_COMPRESSION;
D( kprintf( "bad version number, not '87a' or '89a'\n" ) );
}
}
else
{
error = ERROR_OBJECT_WRONG_TYPE;
D( kprintf( "not a GIF file\n" ) );
}
}
else
{
error = IoErr();
D( kprintf( "error reading magic number\n" ) );
}
/* Prepare decoder for dynamic frame access */
gifdec -> file = gaid -> gaid_FH;
}
else
{
/* No file handle ? - Be sure we got a DTST_RAM sourcetype */
if( sourcetype == DTST_RAM )
{
/* The object is used without any input file.
* This "empty" object is used to run the encoder only...
*/
success = TRUE;
}
else
{
/* No handle ! */
error = ERROR_REQUIRED_ARG_MISSING;
}
}
}
}
else
{
/* can't get required attributes from superclass */
error = ERROR_OBJECT_WRONG_TYPE;
}
}
else
{
/* no memory pool */
error = ERROR_NO_FREE_STORE;
}
SetIoErr( error );
return( success );
}
static
struct FrameNode *AllocFrameNode( struct ClassBase *cb, APTR pool )
{
struct FrameNode *fn;
if( fn = (struct FrameNode *)AllocPooled( pool, (ULONG)sizeof( struct FrameNode ) ) )
{
memset( fn, 0, sizeof( struct FrameNode ) );
}
return( fn );
}
static
struct FrameNode *FindFrameNode( struct MinList *fnl, ULONG timestamp )
{
if( fnl )
{
struct FrameNode *worknode,
*nextnode,
*prevnode;
prevnode = worknode = (struct FrameNode *)(fnl -> mlh_Head);
while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
{
if( (worknode -> fn_TimeStamp) > timestamp )
{
return( prevnode );
}
prevnode = worknode;
worknode = nextnode;
}
if( !IsListEmpty( ((struct List *)fnl) ) )
{
return( prevnode );
}
}
return( NULL );
}
static
void FreeFrameNodeResources( struct ClassBase *cb, struct GIFAnimInstData *gaid )
{
struct FrameNode *worknode;
/* The follwoing was used for debugging */
/* #define FREE_LIST_IN_REVERSE_ORDER 1 */
#ifdef FREE_LIST_IN_REVERSE_ORDER
struct FrameNode *nextnode;
worknode = (struct FrameNode *)(gaid -> gaid_FrameList . mlh_Head);
while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
#else
while( worknode = (struct FrameNode *)RemTail( (struct List *)(&(gaid -> gaid_FrameList)) ) )
#endif /* FREE_LIST_IN_REVERSE_ORDER */
{
if( worknode -> fn_CMap )
{
FreeColorMap( (worknode -> fn_CMap) );
worknode -> fn_CMap = NULL;
}
if( worknode -> fn_BitMap )
{
FreeFrameBitMap( cb, gaid, (worknode -> fn_BitMap) );
worknode -> fn_BitMap = NULL;
}
#ifdef FREE_LIST_IN_REVERSE_ORDER
worknode = nextnode;
#endif /* FREE_LIST_IN_REVERSE_ORDER */
}
}
static
struct BitMap *AllocFrameBitMap( struct ClassBase *cb, struct GIFAnimInstData *gaid )
{
if( gaid -> gaid_UseChunkyMap )
{
return( AllocBitMap( (ULONG)(gaid -> gaid_PaddedWidth), (ULONG)(gaid -> gaid_Height), (ULONG)(gaid -> gaid_Depth),
(BMF_SPECIALFMT | SHIFT_PIXFMT( DIRECTRGB_PIXFMT )), NULL ) );
}
else
{
return( AllocBitMapPooled( cb, (ULONG)(gaid -> gaid_PaddedWidth), (ULONG)(gaid -> gaid_Height), (ULONG)(gaid -> gaid_Depth), (gaid -> gaid_Pool) ) );
}
}
static
void FreeFrameBitMap( struct ClassBase *cb, struct GIFAnimInstData *gaid, struct BitMap *bm )
{
if( bm )
{
if( gaid -> gaid_UseChunkyMap )
{
FreeBitMap( bm );
}
else
{
FreeVecPooled( cb, (gaid -> gaid_Pool), bm );
}
}
}
/* This function assumes (0UL < depth) && (depth <= 8UL) */
static
struct BitMap *AllocBitMapPooled( struct ClassBase *cb, ULONG width, ULONG height, ULONG depth, APTR pool )
{
struct BitMap *bm;
ULONG planesize,
size;
planesize = (ULONG)RASSIZE( width, height ) + 16UL;
size = ((ULONG)sizeof( struct BitMap )) + (planesize * depth) + width;
if( bm = (struct BitMap *)AllocVecPooled( cb, pool, size ) )
{
UWORD pl;
PLANEPTR plane;
InitBitMap( bm, depth, width, height );
plane = (PLANEPTR)(bm + 1); /* First plane follows struct BitMap */
/* Set up plane data */
pl = 0U;
/* Set up plane ptrs */
while( pl < depth )
{
bm -> Planes[ pl ] = plane;
plane = (PLANEPTR)(((UBYTE *)plane) + planesize + 8);
pl++;
}
/* Clear the remaining plane ptrs */
while( pl < 8U )
{
bm -> Planes[ pl ] = NULL;
pl++;
}
}
return( bm );
}
void OpenLogfile( struct ClassBase *cb, struct GIFAnimInstData *gaid )
{
if( (gaid -> gaid_VerboseOutput) == NULL )
{
STRPTR confile;
if( confile = (STRPTR)AllocVec( (((gaid -> gaid_ProjectName)?(strlen( (gaid -> gaid_ProjectName) )):(0UL)) + 100UL), MEMF_PUBLIC ) )
{
mysprintf( cb, confile, "CON:////GIF Anim DataType %s/auto/wait/close/inactive",
((gaid -> gaid_ProjectName)?(FilePart( (gaid -> gaid_ProjectName) )):(NULL)) );
gaid -> gaid_VerboseOutput = Open( confile, MODE_READWRITE );
FreeVec( confile );
}
}
}
void error_printf( struct ClassBase *cb, struct GIFAnimInstData *gaid, STRPTR format, ... )
{
OpenLogfile( cb, gaid );
if( gaid -> gaid_VerboseOutput )
{
VFPrintf( (gaid -> gaid_VerboseOutput), format, (APTR)((&format) + 1) );
}
}
void verbose_printf( struct ClassBase *cb, struct GIFAnimInstData *gaid, STRPTR format, ... )
{
if( gaid -> gaid_VerboseOutput )
{
VFPrintf( (gaid -> gaid_VerboseOutput), format, (APTR)((&format) + 1) );
}
}
static
void AttachSample( struct ClassBase *cb, struct GIFAnimInstData *gaid )
{
if( gaid -> gaid_Sample )
{
struct FrameNode *worknode,
*nextnode;
ULONG period = gaid -> gaid_Period;
ULONG samplesperframe;
BYTE *sample = gaid -> gaid_Sample;
samplesperframe = (((SysBase -> ex_EClockFrequency) * 10UL) / (period * (gaid -> gaid_FPS) * 2UL));
if( gaid -> gaid_SamplesPerFrame )
{
period = (period * samplesperframe) / (gaid -> gaid_SamplesPerFrame);
samplesperframe = gaid -> gaid_SamplesPerFrame;
verbose_printf( cb, gaid, "period corrected from %lu to %lu to match spf=%lu with fps=%lu\n",
(gaid -> gaid_Period), period, samplesperframe, (gaid -> gaid_FPS) );
}
verbose_printf( cb, gaid, "Attching samples (sysclock %lu period %lu fps %lu length %lu samplesperframe %lu)...\n",
(SysBase -> ex_EClockFrequency), period, (gaid -> gaid_FPS), (gaid -> gaid_SampleLength), samplesperframe );
worknode = (struct FrameNode *)(gaid -> gaid_FrameList . mlh_Head);
while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
{
worknode -> fn_Sample = sample;
worknode -> fn_SampleLength = samplesperframe * ((worknode -> fn_Duration) + 1UL);
worknode -> fn_Period = period;
sample += worknode -> fn_SampleLength;
/* End of sample reached ? */
if( (ULONG)(sample - (gaid -> gaid_Sample)) > (gaid -> gaid_SampleLength) )
{
/* Cut last size of sample to fit */
worknode -> fn_SampleLength -= (ULONG)(sample - (gaid -> gaid_Sample));
break;
}
worknode = nextnode;
}
}
}
static
BOOL ReadColorMap( struct ClassBase *cb, struct GIFAnimInstData *gaid, UWORD numcolors, struct ColorRegister *color )
{
struct GIFDecoder *gifdec = (&(gaid -> gaid_GIFDec));
return( (BOOL)(!ReadOK( cb, gifdec, color, (ULONG)(sizeof( struct ColorRegister ) * numcolors) )) );
}
static
void DoExtension( struct ClassBase *cb, Object *o, struct GIFAnimInstData *gaid, TEXT label )
{
struct GIFDecoder *gifdec = (&(gaid -> gaid_GIFDec));
UBYTE buf[ 256 ] = { 0 };
STRPTR str;
switch( label )
{
case 0x01: /* Plain Text Extension */
{
UWORD lpos,
tpos,
width,
height,
cellw,
cellh,
foreground,
background;
error_printf( cb, gaid, "'Plain text extension' not supported yet. Please send this animation to the author that"
"this can be implemented\n" );
(void)GetDataBlock( cb, gaid, buf );
lpos = LOHI2UINT16( buf[ 0 ], buf[ 1 ] );
tpos = LOHI2UINT16( buf[ 2 ], buf[ 3 ] );
width = LOHI2UINT16( buf[ 4 ], buf[ 5 ] );
height = LOHI2UINT16( buf[ 6 ], buf[ 7 ] );
cellw = buf[ 8 ];
cellh = buf[ 9 ];
foreground = buf[ 10 ];
background = buf[ 11 ];
verbose_printf( cb, gaid, "Plain text: "
"left %lu top %lu width %lu height %lu "
"cell width %lu cell height %lu"
"foreground %lu background %lu", lpos, tpos, width, height, cellw, cellh, foreground, background );
while( GetDataBlock( cb, gaid, buf ) != 0 )
{
#if 0
PPM_ASSIGN( image[ ypos ][ xpos ], cmap[ CM_RED ][ v ], cmap[ CM_GREEN ][ v ], cmap[ CM_BLUE ][ v ] );
index++;
#endif
/* Clear buffer for next cycle */
memset( (void *)buf, 0, sizeof( buf ) );
}
return;
}
case 0xf9: /* Graphic Control Extension */
{
STRPTR fmt; /* Format string for verbose output (fmt changes if transparent color is set) */
(void)GetDataBlock( cb, gaid, buf );
/* Get "delta" mode (disposal of previous frame), input flag and the delay time in 1/100 sec) */
gifdec -> Gif89 . disposal = (buf[ 0 ] >> 2) & 0x7;
gifdec -> Gif89 . inputFlag = (buf[ 0 ] >> 1) & 0x1;
gifdec -> Gif89 . delayTime = LOHI2UINT16( buf[ 1 ], buf[ 2 ] );
/* Any transparent color ? */
if( buf[ 0 ] & 0x01 )
{
gifdec -> Gif89 . transparent = buf[ 3 ];
fmt = "Graphic Control Extension: disposal %s (%lu)%s transparent %lu\n";
}
else
{
fmt = "Graphic Control Extension: disposal %s (%lu)%s\n";
}
/* Verbose output ? */
if( gaid -> gaid_VerboseOutput )
{
STRPTR user_input = ((gifdec -> Gif89 . inputFlag)?(" user input requested"):(""));
STRPTR disposal;
switch( gifdec -> Gif89 . disposal )
{
case GIF89A_DISPOSE_NOP: disposal = "nop"; break;
case GIF89A_DISPOSE_NODISPOSE: disposal = "no dispose"; break;
case GIF89A_DISPOSE_RESTOREBACKGROUND: disposal = "restore background"; break;
case GIF89A_DISPOSE_RESTOREPREVIOUS: disposal = "restore previous"; break;
default: disposal = "reserved"; break;
}
verbose_printf( cb, gaid, fmt,
disposal,
(ULONG)(gifdec -> Gif89 . disposal),
user_input,
(gifdec -> Gif89 . transparent) );
}
/* Ignore remaining data... */
while( GetDataBlock( cb, gaid, (UBYTE *)buf ) != 0 )
;
return;
}
case 0xfe: /* Comment Extension */
{
STRPTR annotation;
/* Get all comment extension chunks, and append them on the DTA_ObjAnnotation string we've created before */
while( GetDataBlock( cb, gaid, buf ) != 0 )
{
ULONG size;
STRPTR oldannotation;
buf[ 255 ] = '\0'; /* terminate explicitly */
size = (ULONG)strlen( buf ) + 2UL;
(void)GetDTAttrs( o, DTA_ObjAnnotation, (&oldannotation), TAG_DONE );
if( oldannotation )
{
size += (ULONG)strlen( oldannotation ) + 2UL;
}
/* Allocate a temp buffer */
if( annotation = (STRPTR)AllocMem( size, MEMF_ANY ) )
{
if( oldannotation )
{
strcpy( annotation, oldannotation );
}
else
{
annotation[ 0 ] = '\0'; /* terminate */
}
/* Append the new buffer */
IBMPC2ISOLatin1( buf, (annotation + strlen( annotation )) );
/* Store the comment */
SetDTAttrs( o, NULL, NULL, DTA_ObjAnnotation, annotation, TAG_DONE );
/* Free temp string */
FreeMem( annotation, size );
}
/* Clear buffer for next cycle */
memset( (void *)buf, 0, sizeof( buf ) );
}
/* After all, prompt the annotation to the user */
(void)GetDTAttrs( o, DTA_ObjAnnotation, (&annotation), TAG_DONE );
verbose_printf( cb, gaid, "Comment Extension: '%s'\n", annotation );
return;
}
case 0xff: /* Application Extension */
{
str = "Application Extension";
}
break;
default:
{
mysprintf( cb, buf, "UNKNOWN (0x%02lx)", (long)label );
str = buf;
}
break;
}
verbose_printf( cb, gaid, "got a '%s' extension\n", ((str)?(str):"") );
/* skip extension data */
while( GetDataBlock( cb, gaid, buf ) != 0 )
;
return;
}
static
int GetDataBlock( struct ClassBase *cb, struct GIFAnimInstData *gaid, UBYTE *buf )
{
struct GIFDecoder *gifdec = (&(gaid -> gaid_GIFDec));
UBYTE count;
if( !ReadOK( cb, gifdec, &count, 1 ) )
{
error_printf( cb, gaid, "error in getting DataBlock size\n" );
return( -1 );
}
gifdec -> ZeroDataBlock = (count == 0);
if( (count != 0) && (!ReadOK( cb, gifdec, buf, (ULONG)count ) ) )
{
error_printf( cb, gaid, "error in reading DataBlock\n" );
return( -1 );
}
return( count );
}
static
int GetCode( struct ClassBase *cb, struct GIFAnimInstData *gaid, int code_size, BOOL flag )
{
int i,
j,
ret;
struct GIFDecoder *gifdec = (&(gaid -> gaid_GIFDec));
UBYTE count;
if( flag )
{
gifdec -> GetCode . curbit = 0;
gifdec -> GetCode . lastbit = 0;
gifdec -> GetCode . done = FALSE;
return( 0 );
}
if( (gifdec -> GetCode . curbit + code_size) >= gifdec -> GetCode . lastbit )
{
if( gifdec -> GetCode . done )
{
if( gifdec -> GetCode . curbit >= gifdec -> GetCode . lastbit )
D( kprintf( "ran off the end of my bits\n" ) );
return( -1 );
}
gifdec -> GetCode . buf[ 0 ] = gifdec -> GetCode . buf[ gifdec -> GetCode . last_byte - 2 ];
gifdec -> GetCode . buf[ 1 ] = gifdec -> GetCode . buf[ gifdec -> GetCode . last_byte - 1 ];
if( (count = GetDataBlock( cb, gaid, &gifdec -> GetCode . buf[ 2 ] ) ) == 0 )
gifdec -> GetCode . done = TRUE;
gifdec -> GetCode . last_byte = 2 + count;
gifdec -> GetCode . curbit = (gifdec -> GetCode . curbit - gifdec -> GetCode . lastbit) + 16;
gifdec -> GetCode . lastbit = (2 + count) * 8 ;
}
ret = 0;
for( i = gifdec -> GetCode . curbit, j = 0; j < code_size ; i++, j++ )
ret |= ((gifdec -> GetCode . buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
gifdec -> GetCode . curbit += code_size;
return( ret );
}
static
int LWZReadByte( struct ClassBase *cb, struct GIFAnimInstData *gaid, BOOL flag, int input_code_size )
{
int code,
incode;
register int i;
struct GIFDecoder *gifdec = (&(gaid -> gaid_GIFDec));
if( flag )
{
gifdec -> LWZReadByte . set_code_size = input_code_size;
gifdec -> LWZReadByte . code_size = gifdec -> LWZReadByte . set_code_size + 1;
gifdec -> LWZReadByte . clear_code = 1 << gifdec -> LWZReadByte . set_code_size ;
gifdec -> LWZReadByte . end_code = gifdec -> LWZReadByte . clear_code + 1;
gifdec -> LWZReadByte . max_code_size = 2 * gifdec -> LWZReadByte . clear_code;
gifdec -> LWZReadByte . max_code = gifdec -> LWZReadByte . clear_code + 2;
GetCode( cb, gaid, 0, TRUE );
gifdec -> LWZReadByte . fresh = TRUE;
/* Fill table with the codes... */
for( i = 0 ; i < (gifdec -> LWZReadByte . clear_code) ; i++ )
{
gifdec -> LWZReadByte . table[ 0 ][ i ] = 0;
gifdec -> LWZReadByte . table[ 1 ][ i ] = i;
}
/* ... and clear the remaining part */
for( ; i < (1 << MAX_LWZ_BITS) ; i++ )
{
gifdec -> LWZReadByte . table[ 0 ][ i ] =
gifdec -> LWZReadByte . table[ 1 ][ 0 ] = 0;
}
/* Reset stack ptr */
gifdec -> LWZReadByte . sp = gifdec -> LWZReadByte . stack;
return( 0 );
}
else
{
if( gifdec -> LWZReadByte . fresh )
{
gifdec -> LWZReadByte . fresh = FALSE;
do
{
gifdec -> LWZReadByte . firstcode = gifdec -> LWZReadByte . oldcode = GetCode( cb, gaid, gifdec -> LWZReadByte . code_size, FALSE );
} while( gifdec -> LWZReadByte . firstcode == gifdec -> LWZReadByte . clear_code );
return( gifdec -> LWZReadByte . firstcode );
}
}
if( gifdec -> LWZReadByte . sp > gifdec -> LWZReadByte . stack )
{
return( *--gifdec -> LWZReadByte . sp );
}
while( (code = GetCode( cb, gaid, gifdec -> LWZReadByte . code_size, FALSE )) >= 0 )
{
if( code == gifdec -> LWZReadByte . clear_code )
{
for( i = 0 ; i < gifdec -> LWZReadByte . clear_code ; i++ )
{
gifdec -> LWZReadByte . table[ 0 ][ i ] = 0;
gifdec -> LWZReadByte . table[ 1 ][ i ] = i;
}
for( ; i < (1 << MAX_LWZ_BITS) ; i++ )
{
gifdec -> LWZReadByte . table[ 0 ][ i ] =
gifdec -> LWZReadByte . table[ 1 ][ i ] = 0;
}
gifdec -> LWZReadByte . code_size = gifdec -> LWZReadByte . set_code_size + 1;
gifdec -> LWZReadByte . max_code_size = 2 * gifdec -> LWZReadByte . clear_code;
gifdec -> LWZReadByte . max_code = gifdec -> LWZReadByte . clear_code + 2;
gifdec -> LWZReadByte . sp = gifdec -> LWZReadByte . stack;
gifdec -> LWZReadByte . firstcode =
gifdec -> LWZReadByte . oldcode = GetCode( cb, gaid, gifdec -> LWZReadByte . code_size, FALSE );
return( gifdec -> LWZReadByte . firstcode );
}
else
{
if( code == gifdec -> LWZReadByte . end_code )
{
int count;
UBYTE buf[ 260 ];
if( gifdec -> ZeroDataBlock )
return( -2 );
while( (count = GetDataBlock( cb, gaid, buf )) > 0 )
;
if( count != 0 )
error_printf( cb, gaid, "missing EOD in data stream (common occurence)\n" );
return( -2 );
}
}
incode = code;
if( code >= gifdec -> LWZReadByte . max_code )
{
*gifdec -> LWZReadByte . sp++ = gifdec -> LWZReadByte . firstcode;
code = gifdec -> LWZReadByte . oldcode;
}
while( code >= gifdec -> LWZReadByte . clear_code )
{
*gifdec -> LWZReadByte . sp++ = gifdec -> LWZReadByte . table[ 1 ][ code ];
if( code == gifdec -> LWZReadByte . table[ 0 ][ code ] )
D( kprintf( "circular table entry BIG ERROR\n" ) );
code = gifdec -> LWZReadByte . table[ 0 ][ code ];
}
*gifdec -> LWZReadByte . sp++ = gifdec -> LWZReadByte . firstcode = gifdec -> LWZReadByte . table[ 1 ][ code ];
if( (code = gifdec -> LWZReadByte . max_code) < (1 << MAX_LWZ_BITS ) )
{
gifdec -> LWZReadByte . table[ 0 ][ code ] = gifdec -> LWZReadByte . oldcode;
gifdec -> LWZReadByte . table[ 1 ][ code ] = gifdec -> LWZReadByte . firstcode;
gifdec -> LWZReadByte . max_code++;
if( (gifdec -> LWZReadByte . max_code >= gifdec -> LWZReadByte . max_code_size) && (gifdec -> LWZReadByte . max_code_size < (1 << MAX_LWZ_BITS)) )
{
gifdec -> LWZReadByte . max_code_size *= 2;
gifdec -> LWZReadByte . code_size++;
}
}
gifdec -> LWZReadByte . oldcode = incode;
if( gifdec -> LWZReadByte . sp > gifdec -> LWZReadByte . stack )
{
return( *--gifdec -> LWZReadByte . sp );
}
}
return( code );
}
static
void ReadImage( struct ClassBase *cb, struct GIFAnimInstData *gaid, UBYTE *image,
UWORD imagewidth, UWORD left, UWORD top, UWORD len, UWORD height,
BOOL interlace, BOOL ignore, UWORD transparent )
{
struct GIFDecoder *gifdec = (&(gaid -> gaid_GIFDec));
UBYTE c;
/* Initialize the Compression routines */
if( !ReadOK( cb, gifdec, &c, 1 ) )
D( kprintf( "EOF / read error on image data\n" ) );
/* If this is an "uninteresting picture" ignore it. */
if( ignore )
{
D( kprintf( cb, gaid, "skipping gif image...\n" ) );
/* Loop until end of raster data */
for( ;; )
{
if( !ReadOK( cb, gifdec, &c, 1 ) )
D( kprintf( "EOF / reading block byte count\n" ) );
if( c == 0 )
{
D( kprintf( cb, gaid, "gif image done\n" ) );
break;
}
/* Skip... */
(void)Seek( (gifdec -> file), (long)c, OFFSET_CURRENT );
}
}
else
{
WORD v;
ULONG xpos = 0UL,
ypos = 0UL,
offset = (top * imagewidth) + left,
pass = 0UL;
if( LWZReadByte( cb, gaid, TRUE, c ) < 0 )
D( kprintf( "error reading image\n" ) );
D( kprintf( cb, gaid, "reading %lx %ld.%ld / %ld by %ld%s GIF image\n", image, left, top, len, height, interlace ? " interlaced" : "" ) );
while( (v = LWZReadByte( cb, gaid, FALSE, c )) >= 0 )
{
/* Pixel transparent ? */
if( (transparent == ~0U) ||
(transparent != v) )
{
/* Store pixel */
image[ offset + xpos ] = v;
}
xpos++;
if( xpos == len )
{
xpos = 0UL;
if( interlace )
{
switch( pass )
{
case 0UL:
case 1UL: ypos += 8UL; break;
case 2UL: ypos += 4UL; break;
case 3UL: ypos += 2UL; break;
}
if( ypos >= height )
{
pass++;
switch( pass )
{
case 1UL: ypos = 4UL; break;
case 2UL: ypos = 2UL; break;
case 3UL: ypos = 1UL; break;
default: goto fini;
}
}
}
else
{
ypos++;
}
offset = ((ypos + top) * imagewidth) + left;
}
if( ypos >= height )
break;
}
fini:
if( LWZReadByte( cb, gaid, FALSE, c ) >= 0 )
{
verbose_printf( cb, gaid, "too much input data, ignoring extra...\n" );
}
}
}
/* got from my anim.datatype */
static
struct FrameNode *GetPrevFrameNode( struct FrameNode *currfn, ULONG interleave )
{
struct FrameNode *worknode,
*prevnode;
/* Get previous frame */
worknode = currfn;
while( prevnode = (struct FrameNode *)(worknode -> fn_Node . mln_Pred) )
{
if( (interleave-- == 0U) || ((prevnode -> fn_Node . mln_Pred) == NULL) )
{
break;
}
worknode = prevnode;
}
return( worknode );
}
/* WritePixelArray8 replacement by Peter McGavin (p.mcgavin@irl.cri.nz),
* slightly adapted to fit here...
*/
static
void WriteDeltaPixelArray8Fast( struct BitMap *dest, UBYTE *source, UBYTE *prev )
{
ULONG *plane[ 8 ] = { 0 };
register ULONG *chunky = (ULONG *)source, /* fetch 32 bits per cycle */
*prevchunky = (ULONG *)prev;
ULONG numcycles = ((dest -> Rows) * (dest -> BytesPerRow)) / sizeof( ULONG ),
i;
/* Copy plane ptrs */
for( i = 0UL ; i < (dest -> Depth) ; i++ )
{
plane[ i ] = (ULONG *)(dest -> Planes[ i ]);
}
/* Fill unused planes with plane 0, which will be written last, all previous accesses
* will be droped (assumes that a cache hides this "dummy" writes)
*/
for( i ; i < 8UL ; i++ )
{
plane[ i ] = (ULONG *)(dest -> Planes[ 0 ]);
}
#define merge( a, b, mask, shift ) \
tmp = mask & (a ^ (b >> shift)); \
a ^= tmp; \
b ^= (tmp << shift)
/* Check if we have to do the "delta" test */
if( prevchunky )
{
/* Process bitmaps */
for( i = 0UL ; i < numcycles ; i++ )
{
register ULONG b0, b1, b2, b3, b4, b5, b6, b7,
tmp;
/* process 32 pixels */
b0 = *chunky++; b4 = *chunky++;
b1 = *chunky++; b5 = *chunky++;
b2 = *chunky++; b6 = *chunky++;
b3 = *chunky++; b7 = *chunky++;
/* I use the '+' here to avoid that the compiler skips an expression.
* WARNING: The code assumes that the code is executed in the sequence as it occurs here
*/
if( (b0 != *prevchunky++) + (b4 != *prevchunky++) +
(b1 != *prevchunky++) + (b5 != *prevchunky++) +
(b2 != *prevchunky++) + (b6 != *prevchunky++) +
(b3 != *prevchunky++) + (b7 != *prevchunky++) )
{
merge( b0, b2, 0x0000ffff, 16 );
merge( b1, b3, 0x0000ffff, 16 );
merge( b4, b6, 0x0000ffff, 16 );
merge( b5, b7, 0x0000ffff, 16 );
merge( b0, b1, 0x00ff00ff, 8 );
merge( b2, b3, 0x00ff00ff, 8 );
merge( b4, b5, 0x00ff00ff, 8 );
merge( b6, b7, 0x00ff00ff, 8 );
merge( b0, b4, 0x0f0f0f0f, 4 );
merge( b1, b5, 0x0f0f0f0f, 4 );
merge( b2, b6, 0x0f0f0f0f, 4 );
merge( b3, b7, 0x0f0f0f0f, 4 );
merge( b0, b2, 0x33333333, 2 );
merge( b1, b3, 0x33333333, 2 );
merge( b4, b6, 0x33333333, 2 );
merge( b5, b7, 0x33333333, 2 );
merge( b0, b1, 0x55555555, 1 );
merge( b2, b3, 0x55555555, 1 );
merge( b4, b5, 0x55555555, 1 );
merge( b6, b7, 0x55555555, 1 );
*plane[ 7 ]++ = b0;
*plane[ 6 ]++ = b1;
*plane[ 5 ]++ = b2;
*plane[ 4 ]++ = b3;
*plane[ 3 ]++ = b4;
*plane[ 2 ]++ = b5;
*plane[ 1 ]++ = b6;
*plane[ 0 ]++ = b7;
}
else
{
plane[ 7 ]++;
plane[ 6 ]++;
plane[ 5 ]++;
plane[ 4 ]++;
plane[ 3 ]++;
plane[ 2 ]++;
plane[ 1 ]++;
plane[ 0 ]++;
}
}
}
else
{
/* Process bitmaps */
for( i = 0UL ; i < numcycles ; i++ )
{
register ULONG b0, b1, b2, b3, b4, b5, b6, b7,
tmp;
/* process 32 pixels */
b0 = *chunky++; b4 = *chunky++;
b1 = *chunky++; b5 = *chunky++;
b2 = *chunky++; b6 = *chunky++;
b3 = *chunky++; b7 = *chunky++;
merge( b0, b2, 0x0000ffff, 16 );
merge( b1, b3, 0x0000ffff, 16 );
merge( b4, b6, 0x0000ffff, 16 );
merge( b5, b7, 0x0000ffff, 16 );
merge( b0, b1, 0x00ff00ff, 8 );
merge( b2, b3, 0x00ff00ff, 8 );
merge( b4, b5, 0x00ff00ff, 8 );
merge( b6, b7, 0x00ff00ff, 8 );
merge( b0, b4, 0x0f0f0f0f, 4 );
merge( b1, b5, 0x0f0f0f0f, 4 );
merge( b2, b6, 0x0f0f0f0f, 4 );
merge( b3, b7, 0x0f0f0f0f, 4 );
merge( b0, b2, 0x33333333, 2 );
merge( b1, b3, 0x33333333, 2 );
merge( b4, b6, 0x33333333, 2 );
merge( b5, b7, 0x33333333, 2 );
merge( b0, b1, 0x55555555, 1 );
merge( b2, b3, 0x55555555, 1 );
merge( b4, b5, 0x55555555, 1 );
merge( b6, b7, 0x55555555, 1 );
*plane[ 7 ]++ = b0;
*plane[ 6 ]++ = b1;
*plane[ 5 ]++ = b2;
*plane[ 4 ]++ = b3;
*plane[ 3 ]++ = b4;
*plane[ 2 ]++ = b5;
*plane[ 1 ]++ = b6;
*plane[ 0 ]++ = b7;
}
}
}
static
int getbase2( int x )
{
int i = 0,
j = 1;
while( x > j )
{
j *= 2;
i++;
}
return( i );
}
/* Read and test */
static
BOOL ReadOK( struct ClassBase *cb, struct GIFDecoder *gifdec, void *buffer, ULONG len )
{
if( (gifdec -> which_fh) == WHICHFH_FILE )
{
return( (BOOL)(Read( (gifdec -> file), buffer, len ) == len) );
}
else
{
/* Check if the request fit in out buffer... */
if( (((gifdec -> buffer) - (gifdec -> file_buffer)) + len) <= (gifdec -> buffersize) )
{
CopyMem( (gifdec -> buffer), buffer, len );
gifdec -> buffer += len;
return( TRUE );
}
}
return( FALSE );
}