home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga MA Magazine 1998 #4
/
amigamamagazinepolishissue1998.iso
/
datatypes
/
gifanimdtc201.lha
/
gifanim_datatype
/
encoder.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-03-23
|
33KB
|
1,065 lines
/*
**
** $VER: encoder.c 2.1 (23.3.98)
** gifanim.datatype 2.1
**
** GIF Encoder of gifanim.datatype
**
** 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>
#include <ctype.h>
/*****************************************************************************/
/* a code_int must be able to hold 2**BITS values of type int, and also -1 */
typedef int code_int;
typedef long int count_int;
/* General DEFINEs */
#define HSIZE (5003) /* 80% occupancy */
/*****************************************************************************/
/* encoder context data */
struct GIFEncoder
{
struct ClassBase *classbase;
Object *object;
struct adtFrame loadmsg;
struct BitMap *srcbm[ 2 ];
UWORD whichbm; /* which source bm ? */
struct RastPort rp;
ULONG animwidth,
animheight,
animdepth,
numcolors;
ULONG tpf;
BPTR outfile;
int Width,
Height;
int curx,
cury;
long CountDown;
int Pass/* = 0*/;
int Interlace;
int n_bits; /* number of bits/code */
code_int maxcode; /* maximum code, given n_bits */
count_int htab[ HSIZE ];
unsigned short codetab[ HSIZE ];
code_int free_ent /* = 0*/; /* first unused entry */
/* block compression parameters -- after all codes are used up, and compression rate changes, start over. */
BOOL clear_flg /* = 0*/;
int g_init_bits;
int ClearCode;
int EOFCode;
unsigned long cur_accum/* = 0*/;
int cur_bits/* = 0*/;
char accum[ 256 ]; /* Define the storage for the packet accumulator */
int a_count; /* Number of characters so far in this 'packet' */
};
#define fputc( c, fh ) FPutC( (fh), (long)(c) )
#define EOF (-1)
static const int maxbits = MAX_LWZ_BITS; /* user settable max # bits/code */
static const code_int maxmaxcode = (code_int)1UL << MAX_LWZ_BITS; /* should NEVER generate this code */
#define MAXCODE( nbits ) (((code_int) 1 << (nbits)) - 1)
#define HashTabOf( i ) (genc -> htab[ i ])
#define CodeTabOf( i ) (genc -> codetab[ i ])
static const code_int hsize = HSIZE; /* for dynamic table sizing */
/* To save much memory, we overlay the table used by compress() with those
* used by decompress(). The tab_prefix table is the same size and type
* as the codetab. The tab_suffix table needs 2**MAX_LWZ_BITS characters. We
* get this from the beginning of htab. The output stack uses the rest
* of htab, and contains characters. There is plenty of room for any
* possible stack (stack used to be 8000 characters).
*/
#define tab_prefixof(i) CodeTabOf(i)
#define tab_suffixof(i) ((UBYTE *)(genc -> htab))[i]
#define de_stack ((UBYTE *)&tab_suffixof((code_int)1<<MAX_LWZ_BITS))
/*****************************************************************************/
/* local prototypes */
static struct GIFEncoder *CreateGIFEncoder( struct ClassBase *, Object *, struct GIFAnimInstData *, ULONG, ULONG, ULONG, ULONG, ULONG, BPTR );
static void DeleteGIFEncoder( struct ClassBase *, struct GIFEncoder * );
static int GetPixel( struct GIFEncoder *, int x, int y );
static void BumpPixel( struct GIFEncoder * );
static int GIFNextPixel( struct GIFEncoder * );
static BOOL GIFEncode( struct GIFEncoder *, struct GIFAnimInstData *, BOOL, WORD, WORD, struct ColorRegister *, ULONG, ULONG, ULONG );
static void Putword( struct GIFEncoder *, int );
static void compress( struct GIFEncoder *, int init_bits );
static void output( struct GIFEncoder *, code_int );
static void cl_block( struct GIFEncoder * );
static void cl_hash( struct GIFEncoder *, count_int hsize );
static void char_init( struct GIFEncoder * );
static void char_out( struct GIFEncoder *, int );
static void flush_char( struct GIFEncoder * );
/*****************************************************************************/
ULONG SaveGIFAnim( struct ClassBase *cb, struct IClass *cl, Object *o, struct dtWrite *dtw )
{
ULONG retval = 0UL;
LONG error = 0L;
/* A NULL file handle is a nop (GMultiView uses this to test if a datatype supports RAW writing) */
if( dtw -> dtw_FileHandle )
{
struct GIFAnimInstData *gaid = (struct GIFAnimInstData *)INST_DATA( cl, o );
ULONG modeid;
struct ColorRegister *cm;
ULONG numcolors;
ULONG startframe = 0UL,
numframes = 0UL,
framestep = 1UL;
ULONG tpf = 0UL;
ULONG animwidth,
animheight,
animdepth;
if( GetDTAttrs( o, ADTA_ModeID, (&modeid),
ADTA_ColorRegisters, (&cm),
ADTA_NumColors, (&numcolors),
ADTA_Width, (&animwidth),
ADTA_Height, (&animheight),
ADTA_Depth, (&animdepth),
ADTA_Frame, (&startframe),
ADTA_Frames, (&numframes),
ADTA_TicksPerFrame, (&tpf),
TAG_DONE ) == 9UL )
{
verbose_printf( cb, gaid, "anim %lu*%lu*%lu, %lu colors, %lu frames, tpf %lu\n",
animwidth, animheight, animdepth, numcolors, numframes, tpf );
/* GIF only supports up to 256 colors (8 bit) */
if( animdepth <= 8UL )
{
/* HAM cannot be implemented; EHB would be possible (on request) */
if( !(modeid & (HAM | EXTRA_HALFBRITE)) )
{
struct GIFEncoder *genc;
if( genc = CreateGIFEncoder( cb, o, gaid, animwidth, animheight, animdepth, numcolors, tpf, (dtw -> dtw_FileHandle) ) )
{
struct TagItem *tstate,
*ti;
numframes -= startframe;
tstate = dtw -> dtw_AttrList;
while( ti = NextTagItem( (&tstate) ) )
{
switch( ti -> ti_Tag )
{
case ADTA_Frame: startframe = ti -> ti_Data; break;
case ADTA_Frames: numframes = ti -> ti_Data; break;
case ADTA_FrameIncrement: framestep = ti -> ti_Data; break;
}
}
if( framestep == 0UL ) framestep = 1UL;
error_printf( cb, gaid, "saving gif anim %lu %lu %lu\n", startframe, numframes, framestep );
if( numframes )
{
retval = (LONG)GIFEncode( genc, gaid, FALSE, 0, -1, cm, startframe, numframes, framestep );
error = IoErr();
}
DeleteGIFEncoder( cb, genc );
}
else
{
/* Can't alloc gif encoder context data */
error = ERROR_NO_FREE_STORE;
error_printf( cb, gaid, "no memory for encoder context data\n" );
}
}
else
{
error_printf( cb, gaid, "HAM/EHB not supported by the GIF format\n" );
error = (modeid & EXTRA_HALFBRITE)?(ERROR_NOT_IMPLEMENTED):(DTERROR_INVALID_DATA);
}
}
else
{
error_printf( cb, gaid, "Object is too deep (%lu planes), gif encoder works only with depth <= 8\n", animdepth );
error = DTERROR_INVALID_DATA;
}
}
else
{
/* can't get required attributes from object */
error_printf( cb, gaid, "not enougth attributes\n" );
error = ERROR_OBJECT_WRONG_TYPE;
}
/* Some info... */
if( retval )
{
verbose_printf( cb, gaid, "GIF Animation successfully created\n" );
}
else
{
error_printf( cb, gaid, "GIF Animation creation failed, error %ld\n", error );
}
}
SetIoErr( error );
return( retval );
}
/*****************************************************************************/
static
struct GIFEncoder *CreateGIFEncoder( struct ClassBase *cb, Object *o, struct GIFAnimInstData *gaid, ULONG animwidth, ULONG animheight, ULONG animdepth, ULONG numcolors, ULONG tpf, BPTR out )
{
struct GIFEncoder *genc;
if( genc = (struct GIFEncoder *)AllocVec( (ULONG)sizeof( struct GIFEncoder ), (MEMF_PUBLIC | MEMF_CLEAR) ) )
{
/* Idiot testing... */
if( o && animwidth && animheight && animdepth && (numcolors > 0UL) && (numcolors <= MAXCOLORMAPSIZE) && tpf )
{
genc -> srcbm[ 0 ] = AllocBitMap( animwidth, animheight, animdepth, BMF_MINPLANES, NULL );
genc -> srcbm[ 1 ] = AllocBitMap( animwidth, animheight, animdepth, BMF_MINPLANES, NULL );
if( (genc -> srcbm[ 0 ]) && (genc -> srcbm[ 1 ]) )
{
genc -> classbase = cb;
genc -> object = o;
genc -> outfile = out;
genc -> animwidth = animwidth;
genc -> animheight = animheight;
genc -> animdepth = animdepth;
genc -> numcolors = numcolors;
genc -> tpf = tpf;
/* Init source rastport */
InitRastPort( (&(genc -> rp)) );
genc -> rp . BitMap = genc -> srcbm[ 0 ]; /* genc -> whichbm is 0 here... */
return( genc );
}
else
{
error_printf( cb, gaid, "no memory for bitmap\n" );
}
FreeBitMap( (genc -> srcbm[ 1 ]) );
FreeBitMap( (genc -> srcbm[ 0 ]) );
}
else
{
error_printf( cb, gaid, "invalid arguments\n" );
}
FreeVec( genc );
}
return( NULL );
}
static
void DeleteGIFEncoder( struct ClassBase *cb, struct GIFEncoder *genc )
{
if( genc )
{
/* Unload any loaded frame... */
if( genc -> loadmsg . alf_UserData )
{
genc -> loadmsg . MethodID = ADTM_UNLOADFRAME;
DoMethodA( (genc -> object), (Msg)(&(genc -> loadmsg)) );
}
FreeBitMap( (genc -> srcbm[ 1 ]) );
FreeBitMap( (genc -> srcbm[ 0 ]) );
FreeVec( genc );
}
}
/*****************************************************************************/
/* here begins the real encoder code... */
#define cb (genc -> classbase)
static
int GetPixel( struct GIFEncoder *genc, int x, int y )
{
int pixel = (int)ReadPixel( (&(genc -> rp)), (long)x, (long)y );
/* Should not occur, but... */
if( pixel >= (1UL << (genc -> animdepth)) )
{
pixel = 0;
}
return( pixel );
}
/* Bump the 'curx' and 'cury' to point to the next pixel */
static
void BumpPixel( struct GIFEncoder *genc )
{
/* Bump the current X position */
genc -> curx++;
/* If we are at the end of a scan line, set curx back to the beginning
* If we are interlaced, bump the cury to the appropriate spot,
* otherwise, just increment it.
*/
if( (genc -> curx) == (genc -> Width) )
{
genc -> curx = 0;
if( genc -> Interlace )
{
switch( genc -> Pass )
{
case 0:
{
genc -> cury += 8;
if( (genc -> cury) >= (genc -> Height) )
{
genc -> Pass++;
genc -> cury = 4;
}
}
break;
case 1:
{
genc -> cury += 8;
if( (genc -> cury) >= (genc -> Height) )
{
genc -> Pass++;
genc -> cury = 2;
}
}
break;
case 2:
{
genc -> cury += 4;
if( (genc -> cury) >= (genc -> Height) )
{
genc -> Pass++;
genc -> cury = 1;
}
}
break;
case 3:
{
genc -> cury += 2;
}
break;
}
}
else
{
genc -> cury++;
}
}
}
/* Return the next pixel from the image */
static
int GIFNextPixel( struct GIFEncoder *genc )
{
int r;
if( (genc -> CountDown) == 0 )
return( EOF );
--genc -> CountDown;
r = GetPixel( genc, (genc -> curx), (genc -> cury) );
BumpPixel( genc );
return( r );
}
/* public */
static
BOOL GIFEncode( struct GIFEncoder *genc, struct GIFAnimInstData *gaid,
BOOL GInterlace, WORD Background, WORD Transparent,
struct ColorRegister *cm, ULONG startframe, ULONG numframes, ULONG framestep )
{
ULONG timestamp;
int B;
int RWidth,
RHeight;
int Resolution;
int ColorMapSize;
int InitCodeSize;
int i;
BPTR fp = genc -> outfile;
LONG gif_delay = 0L;
BOOL save_frame;
struct ColorRegister localcolormap[ MAXCOLORMAPSIZE ];
BOOL save_localcolormap;
BOOL firstframe = TRUE; /* the first frame has not been written yet */
genc -> Interlace = GInterlace;
ColorMapSize = 1UL << (genc -> animdepth);
RWidth = genc -> Width = genc -> animwidth;
RHeight = genc -> Height = genc -> animheight;
Resolution = genc -> animdepth;
/* The initial code size */
if( (genc -> animdepth) <= 1 )
InitCodeSize = 2;
else
InitCodeSize = (genc -> animdepth);
/* Write the GIF signature */
FWrite( fp, "GIF", 1, 3 );
/* Write the GIF version number (only "89a" because of the "Graphics Control Extension" used for timing) */
FWrite( fp, "89a", 1, 3 );
/* Write out the screen width and height */
Putword( genc, RWidth );
Putword( genc, RHeight );
/* Flag field */
{
/* Indicate that there is a global colour map */
B = LOCALCOLORMAP; /* Yes, there is a global color map */
/* OR in the resolution */
B |= (Resolution - 1) << 5;
/* OR in the Bits per Pixel (Size of global color map) */
B |= ((genc -> animdepth) - 1);
/* Write it out */
fputc( B, fp );
}
/* Write out the Background colour */
fputc( Background, fp );
/* Write pixel aspect ratio (not implemented yet, therefore 0 here) */
fputc( 0, fp );
/* Write out the Global Colour Map */
for( i = 0 ; i < ColorMapSize ; i++ )
{
fputc( (cm[ i ] . red), fp );
fputc( (cm[ i ] . green), fp );
fputc( (cm[ i ] . blue), fp );
}
/* Write comment extensions for each DTA_Obj#? attribute we found... */
{
/* ti_Tag is the attribute to check; ti_Data is the label written in front of the data */
struct TagItem commentstags[] =
{
{ DTA_ObjName, (ULONG)"name: " },
{ DTA_ObjAuthor, (ULONG)"author: " },
{ DTA_ObjAnnotation, (ULONG)"annotation: " },
{ DTA_ObjCopyright, (ULONG)"copyright: " },
{ DTA_ObjVersion, (ULONG)"version: " },
{ TAG_DONE, 0UL }
};
struct TagItem *tstate = commentstags,
*ti;
while( ti = NextTagItem( (&tstate) ) )
{
STRPTR string,
label = (STRPTR)(ti -> ti_Data);
(void)GetDTAttrs( (genc -> object), (ti -> ti_Tag), (&string), TAG_DONE );
if( string )
{
UBYTE *c;
fputc( '!', fp ); /* Extension */
fputc( 0xfe, fp ); /* GIF89a Comment extension */
c = label; while( *c ) char_out( genc, *c++ ); /* write comment label */
c = string; while( *c ) char_out( genc, *c++ ); /* write DTA_Obj#? attribute data */
flush_char( genc );
fputc( 0, fp ); /* Block terminator */
}
}
}
for( timestamp = startframe ; numframes > 0UL ; timestamp += framestep, numframes-- )
{
/* reset some values */
save_frame = FALSE;
save_localcolormap = FALSE;
/* Check for abort... */
if( CheckSignal( SIGBREAKF_CTRL_D ) )
{
SetIoErr( ERROR_BREAK );
return( FALSE );
}
genc -> loadmsg . MethodID = ADTM_LOADFRAME;
genc -> loadmsg . alf_TimeStamp = timestamp;
genc -> loadmsg . alf_Frame = timestamp; /* CBM anim.datatype compatibility ONLY */
if( DoMethodA( (genc -> object), (Msg)(&(genc -> loadmsg)) ) )
{
/* print frame contents */
verbose_printf( cb, gaid, "frame: timestamp %lu frame %lu duration %lu bitmap %lx cmap %lx sample %lx len %lu period %lu\n",
timestamp,
(genc -> loadmsg . alf_Frame),
(genc -> loadmsg . alf_Duration),
(genc -> loadmsg . alf_BitMap),
(genc -> loadmsg . alf_CMap),
(genc -> loadmsg . alf_Sample),
(genc -> loadmsg . alf_SampleLength),
(genc -> loadmsg . alf_Period) );
/* If we got a bitmap (should always be TRUE), compare it with the previous one
* If there was a change of the contents, write a matching gif image out...
*/
if( genc -> loadmsg . alf_BitMap )
{
struct BitMap *bm;
/* swap bitmaps */
genc -> whichbm = !(genc -> whichbm); /* 0 to 1 or 1 to 0 */
bm = (genc -> srcbm[ (genc -> whichbm) ]); /* shortcut */
/* Following GetPixel calls should use this bitmap */
genc -> rp . BitMap = bm;
CopyBitMap( cb, bm, (genc -> loadmsg . alf_BitMap), (genc -> animwidth), (genc -> animheight) );
/* The first frame must be written because ther's no previous frame to compare... */
if( firstframe )
{
firstframe = FALSE;
save_frame = TRUE;
}
else
{
/* Compare old and new bitmap and check if they're equal
* If they're equal, there's no need to write them out...
*/
save_frame = !CmpBitMap( cb, (genc -> srcbm[ (genc -> whichbm) ]), (genc -> srcbm[ !(genc -> whichbm) ]), (genc -> animwidth), (genc -> animheight) );
}
}
else
{
/* Should not occur */
error_printf( cb, gaid, "WARNING: no bitmap\n" );
SetIoErr( ERROR_OBJECT_WRONG_TYPE ); /* not very meaningfull */
return( FALSE );
}
/* If we got a local colormap, check if it is different from the global one.
* If there is a difference, set "save_localcolormap" to TRUE that a local colormap is saved below
*/
if( genc -> loadmsg . alf_CMap )
{
ULONG color;
ULONG rgb[ 3 ];
for( color = 0UL ; color < ColorMapSize ; color++ )
{
GetRGB32( (genc -> loadmsg . alf_CMap), color, 1UL, rgb );
localcolormap[ color ] . red = (UBYTE)(rgb[ 0 ] >> 24UL);
localcolormap[ color ] . green = (UBYTE)(rgb[ 1 ] >> 24UL);
localcolormap[ color ] . blue = (UBYTE)(rgb[ 2 ] >> 24UL);
/* Any different between alf_CMap and global color map (e.g. ADTA_ColorRegisters) ? */
if( ((localcolormap[ color ] . red) != (cm[ color ] . red)) ||
((localcolormap[ color ] . green) != (cm[ color ] . green)) ||
((localcolormap[ color ] . blue) != (cm[ color ] . blue)) )
{
/* Save local colormap */
save_localcolormap = TRUE;
}
}
}
/* Any change in the data ? */
if( save_frame || save_localcolormap )
{
verbose_printf( cb, gaid, "saving gif image%s, %ld/100 sec delay\n",
((save_localcolormap)?(" with local colormap"):("")),
gif_delay );
/* Calculate number of bits we are expecting */
genc -> CountDown = (long)(genc -> Width) * (long)(genc -> Height);
/* Set up the current x and y position */
genc -> curx = genc -> cury = 0;
/* Indicate which pass we are on (if interlace) */
genc -> Pass = 0;
/* Reset */
genc -> cur_accum = 0;
genc -> cur_bits = 0;
genc -> a_count = 0;
/* Write out Graphic Control Extension for timing (and transparent colour index, if necessary.) */
{
UBYTE packed = 0U;
fputc( '!', fp ); /* Extension */
fputc( 0xf9, fp ); /* GIF89a Graphic Control Extension */
fputc( 4, fp ); /* Block size */
if( Transparent >= 0 )
{
packed |= 0x01; /* has transparent color ! */
}
fputc( packed, fp );
Putword( genc, (int)gif_delay ); /* Delay in 1/100 sec */
fputc( (Transparent >= 0)?(Transparent):(0), fp );
fputc( 0, fp ); /* Block terminator */
gif_delay = 0L; /* delay time written... */
}
/* Write GIF Image Descriptor */
{
UBYTE packed = 0U;
/* Write: "Start of raster data" */
fputc( ',', fp );
/* Write the Image header (does currently not support deltas) */
Putword( genc, 0 );
Putword( genc, 0 );
Putword( genc, (genc -> Width) );
Putword( genc, (genc -> Height) );
/* Write out whether or not the image is interlaced */
if( genc -> Interlace )
{
packed |= INTERLACE;
}
/* Write out whether or not the image has an own colormap */
if( save_localcolormap )
{
packed |= LOCALCOLORMAP;
/* OR in the Bits per Pixel (Size of local color map) */
packed |= ((genc -> animdepth) - 1);
}
fputc( packed, fp );
}
/* Write local color map ? */
if( save_localcolormap )
{
ULONG color;
/* Write out the Local Colour Map */
for( color = 0UL ; color < ColorMapSize ; color++ )
{
fputc( (localcolormap[ color ] . red), fp );
fputc( (localcolormap[ color ] . green), fp );
fputc( (localcolormap[ color ] . blue), fp );
}
}
/* Write out the initial code size */
fputc( InitCodeSize, fp );
/* Go and actually compress the data */
compress( genc, (InitCodeSize + 1) );
/* Write out a Zero-length packet (to end the series) */
fputc( 0, fp );
}
/* Sum here the time to wait for this frame...
* (and Scale from 1/1200 units (realtime.library && ADTA_TicksPerFrame) to GIF delay (1/100) units)
*/
gif_delay += INTDIVR( (100UL * (genc -> tpf)), TICK_FREQ );
}
else
{
/* ADTM_LOADFRAME failed: Result2 contains the error cause */
return( FALSE );
}
}
/* Write the GIF file terminator */
fputc( ';', fp );
return( TRUE );
}
/* Write out a LITTLE_ENDIAN word to the GIF file */
static
void Putword( struct GIFEncoder *genc, int w )
{
fputc( (w & 0xff), (genc -> outfile) );
fputc( ((w / 256) & 0xff), (genc -> outfile) );
}
/***************************************************************************
*
* GIFCOMPR.C - GIF Image compression routines
*
* Lempel-Ziv compression based on 'compress'. GIF modifications by
* David Rowley (mgardi@watdcsu.waterloo.edu)
*
***************************************************************************/
/*
*
* GIF Image compression - modified 'compress'
*
* Based on: compress.c - File compression ala IEEE Computer, June 1984.
*
* By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
* Jim McKie (decvax!mcvax!jim)
* Steve Davies (decvax!vax135!petsd!peora!srd)
* Ken Turkowski (decvax!decwrl!turtlevax!ken)
* James A. Woods (decvax!ihnp4!ames!jaw)
* Joe Orost (decvax!vax135!petsd!joe)
*
*/
/*
* compress stdin to stdout
*
* Algorithm: use open addressing double hashing (no chaining) on the
* prefix code / next character combination. We do a variant of Knuth's
* algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
* secondary probe. Here, the modular division first probe is gives way
* to a faster exclusive-or manipulation. Also do block compression with
* an adaptive reset, whereby the code table is cleared when the compression
* ratio decreases, but after the table fills. The variable-length output
* codes are re-sized at this point, and a special CLEAR code is generated
* for the decompressor. Late addition: construct the table according to
* file size for noticeable speed improvement on small files. Please direct
* questions about this implementation to ames!jaw.
*/
static
void compress( struct GIFEncoder *genc, int init_bits )
{
register long fcode;
register code_int i /* = 0 */;
register int c;
register code_int ent;
register code_int disp;
register code_int hsize_reg;
register int hshift;
/* Set up the globals: g_init_bits - initial number of bits */
genc -> g_init_bits = init_bits;
/* Set up the necessary values */
genc -> clear_flg = FALSE;
genc -> maxcode = MAXCODE((genc -> n_bits) = (genc -> g_init_bits));
genc -> ClearCode = (1 << (init_bits - 1));
genc -> EOFCode = genc -> ClearCode + 1;
genc -> free_ent = genc -> ClearCode + 2;
char_init( genc );
ent = GIFNextPixel( genc );
for( fcode = (long)hsize, hshift = 0 ; fcode < 65536L ; fcode *= 2L )
{
hshift++;
}
hshift = 8 - hshift; /* set hash code range bound */
hsize_reg = hsize;
cl_hash( genc, (count_int)hsize_reg ); /* clear hash table */
output( genc, (code_int)(genc -> ClearCode) );
while( (c = GIFNextPixel( genc )) != EOF )
{
fcode = (long) (((long) c << maxbits) + ent);
i = (((code_int)c << hshift) ^ ent); /* xor hashing */
if( HashTabOf (i) == fcode )
{
ent = CodeTabOf (i);
continue;
}
else
if ( (long)HashTabOf (i) < 0 ) /* empty slot */
goto nomatch;
disp = hsize_reg - i; /* secondary hash (after G. Knott) */
if ( i == 0 )
disp = 1;
probe:
if ( (i -= disp) < 0 )
i += hsize_reg;
if ( HashTabOf (i) == fcode )
{
ent = CodeTabOf (i);
continue;
}
if( (long)HashTabOf (i) > 0 )
goto probe;
nomatch:
output( genc, (code_int) ent );
ent = c;
if( (genc -> free_ent) < maxmaxcode )
{
CodeTabOf( i ) = genc -> free_ent++; /* code -> hashtable */
HashTabOf( i ) = fcode;
}
else
{
cl_block( genc );
}
}
/* Put out the final code. */
output( genc, (code_int)ent );
output( genc, (code_int)(genc -> EOFCode) );
}
/*****************************************************************
* TAG( output )
*
* Output the given code.
* Inputs:
* code: A n_bits-bit integer. If == -1, then EOF. This assumes
* that n_bits =< (long)wordsize - 1.
* Outputs:
* Outputs code to the file.
* Assumptions:
* Chars are 8 bits long.
* Algorithm:
* Maintain a BITS character long buffer (so that 8 codes will
* fit in it exactly). Use the VAX insv instruction to insert each
* code in turn. When the buffer fills up empty it and start over.
*/
static const
unsigned long masks[] =
{
0x0000, 0x0001, 0x0003, 0x0007,
0x000F, 0x001F, 0x003F, 0x007F,
0x00FF, 0x01FF, 0x03FF, 0x07FF,
0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,
0xFFFF
};
static
void output( struct GIFEncoder *genc, code_int code )
{
genc -> cur_accum &= masks[ genc -> cur_bits ];
if( genc -> cur_bits > 0 )
genc -> cur_accum |= ((long)code << genc -> cur_bits);
else
genc -> cur_accum = code;
genc -> cur_bits += genc -> n_bits;
while( genc -> cur_bits >= 8 )
{
char_out( genc, (unsigned int)(genc -> cur_accum & 0xff) );
genc -> cur_accum >>= 8;
genc -> cur_bits -= 8;
}
/* If the next entry is going to be too big for the code size,
* then increase it, if possible.
*/
if( ((genc -> free_ent) > (genc -> maxcode)) || (genc -> clear_flg) )
{
if( genc -> clear_flg )
{
genc -> maxcode = MAXCODE ((genc -> n_bits) = (genc -> g_init_bits));
genc -> clear_flg = FALSE;
}
else
{
++genc -> n_bits;
if( (genc -> n_bits) == maxbits )
genc -> maxcode = maxmaxcode;
else
genc -> maxcode = MAXCODE(genc -> n_bits);
}
}
if( code == (genc -> EOFCode) )
{
/* At EOF, write the rest of the buffer. */
while( genc -> cur_bits > 0 )
{
char_out( genc, (unsigned int)(genc -> cur_accum & 0xff) );
genc -> cur_accum >>= 8;
genc -> cur_bits -= 8;
}
flush_char( genc );
}
}
/* Clear out the hash table */
static
void cl_block( struct GIFEncoder *genc ) /* table clear for block compress */
{
cl_hash ( genc, (count_int) hsize );
genc -> free_ent = (genc -> ClearCode) + 2;
genc -> clear_flg = TRUE;
output( genc, (code_int)(genc -> ClearCode) );
}
static
void cl_hash( struct GIFEncoder *genc, register count_int hsize ) /* reset code table */
{
register count_int *htab_p = (genc -> htab) + hsize;
register long i;
register long m1 = -1;
i = hsize - 16;
do /* might use Sys V memset(3) here */
{
*(htab_p-16) = m1;
*(htab_p-15) = m1;
*(htab_p-14) = m1;
*(htab_p-13) = m1;
*(htab_p-12) = m1;
*(htab_p-11) = m1;
*(htab_p-10) = m1;
*(htab_p- 9) = m1;
*(htab_p- 8) = m1;
*(htab_p- 7) = m1;
*(htab_p- 6) = m1;
*(htab_p- 5) = m1;
*(htab_p- 4) = m1;
*(htab_p- 3) = m1;
*(htab_p- 2) = m1;
*(htab_p- 1) = m1;
htab_p -= 16;
} while ((i -= 16) >= 0);
for ( i += 16; i > 0; --i )
*--htab_p = m1;
}
/******************************************************************************
*
* GIF Specific routines
*
******************************************************************************/
/* Set up the 'byte output' routine */
static
void char_init( struct GIFEncoder *genc )
{
genc -> a_count = 0;
}
/* Add a character to the end of the current packet, and if it is 254
* characters, flush the packet to disk.
*/
static
void char_out( struct GIFEncoder *genc, int c )
{
genc -> accum[ (genc -> a_count)++ ] = c;
if( (genc -> a_count) >= 254 )
flush_char( genc );
}
/* Flush the packet to disk, and reset the accumulator */
static
void flush_char( struct GIFEncoder *genc )
{
if( (genc -> a_count) > 0 )
{
fputc( (genc -> a_count), (genc -> outfile) );
FWrite( (genc -> outfile), (genc -> accum), 1UL, (ULONG)(genc -> a_count) );
genc -> a_count = 0;
}
}