home *** CD-ROM | disk | FTP | other *** search
-
- /*
- **
- ** $VER: encoder.c 2.2 (13.4.98)
- ** gifanim.datatype 2.2
- **
- ** 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"
-
- /* encoder header */
- #include "encoder.h"
-
- /*****************************************************************************/
-
- #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 *, struct BitMap *, 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;
- struct BitMap *keyframe;
-
- if( GetDTAttrs( o, ADTA_ModeID, (&modeid),
- ADTA_ColorRegisters, (&cm),
- ADTA_NumColors, (&numcolors),
- ADTA_Width, (&animwidth),
- ADTA_Height, (&animheight),
- ADTA_Depth, (&animdepth),
- ADTA_KeyFrame, (&keyframe),
- ADTA_Frame, (&startframe),
- ADTA_Frames, (&numframes),
- ADTA_TicksPerFrame, (&tpf),
- TAG_DONE ) == 10UL )
- {
- 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 (same problem as truecolor support); EHB would be possible (on request) */
- if( !(modeid & (HAM | EXTRA_HALFBRITE)) )
- {
- struct GIFEncoder *genc;
-
- if( genc = CreateGIFEncoder( cb, o, gaid, keyframe, 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, (genc -> interlace), (genc -> backgroundpen), (genc -> transparentpen), 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, struct BitMap *keybm, ULONG animwidth, ULONG animheight, ULONG animdepth, ULONG numcolors, ULONG tpf, BPTR out )
- {
- /* Idiot testing... */
- if( o && animwidth && animheight && animdepth && (numcolors > 0UL) && (numcolors <= MAXCOLORMAPSIZE) && tpf && keybm )
- {
- ULONG chunkysize = (animwidth + 2UL) * (animheight + 2UL) + 256; /* size of one chunkymap with safety space */
- struct GIFEncoder *genc;
- ULONG size1,
- size2,
- msize;
-
- /* Compute amount of memory needed for the gif encoder... */
- size1 = sizeof( struct GIFEncoder ) + 8UL; /* Encoder context data */
- size2 = size1 + chunkysize + 8UL; /* Chunky map 1 */
- msize = size2 + chunkysize + 8UL; /* Chunky map 2 */
-
- if( genc = (struct GIFEncoder *)AllocVec( msize, (MEMF_PUBLIC | MEMF_CLEAR) ) )
- {
- /* get the chunkymap ptrs... */
- genc -> srcchunkymap[ 0 ] = MEMORY_NAL_FOLLOWING( genc, size1 );
- genc -> srcchunkymap[ 1 ] = MEMORY_NAL_FOLLOWING( genc, size2 );
-
- /* Init the temp. rastport required by ReadPixelArray8 */
- InitRastPort( (&(genc -> rpa8tmprp)) );
- genc -> rpa8tmprp . BitMap = AllocBitMap( ((animwidth + 15UL) & ~15UL), 1UL, animdepth, BMF_MINPLANES, keybm );
-
- /* Get a system bitmap (where RPA8 can read from...) */
- genc -> srcbm = AllocBitMap( animwidth, animheight, animdepth, BMF_MINPLANES, keybm );
-
- if( (genc -> srcbm) && (genc -> rpa8tmprp . BitMap) )
- {
- /* Fill-in the remaining ptrs... */
- genc -> classbase = cb;
- genc -> object = o;
- genc -> outfile = out;
- genc -> animwidth = animwidth;
- genc -> animheight = animheight;
- genc -> animdepth = animdepth;
- genc -> numcolors = numcolors;
- genc -> tpf = tpf;
-
- /* Prefs defaults... */
- genc -> interlace = FALSE;
- genc -> backgroundpen = 0; /* default bg pen */
- genc -> transparentpen = -1; /* means: no transparent pen */
-
- /* Read encoder preferences */
- ReadENVPrefs( cb, gaid, genc );
-
- /* Init source rastport */
- InitRastPort( (&(genc -> rp)) );
- genc -> rp . BitMap = genc -> srcbm;
-
- return( genc );
- }
- else
- {
- error_printf( cb, gaid, "no memory for temporary bitmap(s)\n" );
- }
-
- FreeBitMap( (genc -> rpa8tmprp . BitMap) );
- FreeBitMap( (genc -> srcbm) );
- }
- else
- {
- error_printf( cb, gaid, "no memory for encoder context data\n" );
- }
-
- FreeVec( genc );
- }
- else
- {
- error_printf( cb, gaid, "invalid arguments\n" );
- }
-
-
- 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 -> rpa8tmprp . BitMap) );
- FreeBitMap( (genc -> srcbm) );
-
- FreeVec( genc );
- }
- }
-
- /*****************************************************************************/
-
- /* here begins the real encoder code... */
- #define cb (genc -> classbase)
-
- static
- int GetPixel( struct GIFEncoder *genc, int x, int y )
- {
- #if 1
- int pixel = (int)ReadPixel( (&(genc -> rp)), (long)x, (long)y );
- #else
- int pixel = (int)genc -> currchunkymap[ ((genc -> animwidth) * y) + x ];
- #endif
-
- /* 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 )
- {
- /* swap bitmaps */
- genc -> whichbm = !(genc -> whichbm); /* 0 to 1 or 1 to 0 */
-
- genc -> currchunkymap = genc -> srcchunkymap[ (genc -> whichbm) ]; /* shortcut */
-
- /* Copy given bitmap (which may be a planar bitmap in fast-mem) to a system-allocated bitmap
- * that ReadPixel(%|Array8) can reach it...
- */
- CopyBitMap( cb, (genc -> srcbm), (genc -> loadmsg . alf_BitMap), (genc -> animwidth), (genc -> animheight) );
-
- /* Convert data into a chunky pixel map */
- (void)ReadPixelArray8( (&(genc -> rp)), 0UL, 0UL, ((genc -> animwidth) - 1UL), ((genc -> animheight) - 1UL), (genc -> currchunkymap), (&(genc -> rpa8tmprp)) );
-
- /* 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 chunky map and check if they're equal
- * If they're equal, there's no need to write them out...
- */
- save_frame = memcmp( (genc -> srcchunkymap[ (genc -> whichbm) ]),
- (genc -> srcchunkymap[ !(genc -> whichbm) ]),
- (size_t)((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;
- }
- }
-
-
-