/******************************************************************************
*
* $Id$
*
*
* Copyright (C) 1997-2009 by Dimitri van Heesch.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation under the terms of the GNU General Public License is hereby
* granted. No representations are made about the suitability of this software
* for any purpose. It is provided "as is" without express or implied warranty.
* See the GNU General Public License for more details.
*
* All output generated with Doxygen is not covered by this license.
*
* The GIF compression code below is based on the file ppmtogif.c of the
* netpbm package. The original copyright message follows:
*
* ---------------------------------------------------------------------------
*
* Copyright (C) 1989 by Jef Poskanzer.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. This software is provided "as is" without express or
* implied warranty.
*
* ---------------------------------------------------------------------------
*
* The Graphics Interchange Format(c) is the Copyright property of
* CompuServe Incorporated. GIF(sm) is a Service Mark property of
* CompuServe Incorporated.
*/
#include "gifenc.h"
static const unsigned int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
0x001F, 0x003F, 0x007F, 0x00FF,
0x01FF, 0x03FF, 0x07FF, 0x0FFF,
0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
GifEncoder::GifEncoder(Byte *rawBytes,Color *p,int w,int h, Byte d,
int t)
: colorResolution(8),globalPaletteFlag(0x80),bits(12),
maxMaxCode(1<<bits)
{
width = w;
height = h;
depth = d;
transIndex = t;
palette = p;
data = rawBytes;
dataPtr = data;
}
GifEncoder::~GifEncoder()
{
}
void GifEncoder::writeGIF(QFile &fp)
{
// Write the Magic header
fp.writeBlock( transIndex < 0 ? "GIF87a" : "GIF89a", 6 );
// Write the logical screen descriptor
putWord( width, fp );
putWord( height, fp );
Byte pack = globalPaletteFlag | ((colorResolution-1)<<4) | (depth-1);
putByte( pack, fp );
putByte( 0, fp ); // the background color
putByte( 0, fp ); // no aspect ration defined
// Write global color table
int i; for ( i=0 ; i< (1<<depth) ; i++)
{
putByte(palette[i].red, fp);
putByte(palette[i].green,fp);
putByte(palette[i].blue, fp);
}
if ( transIndex >= 0)
{
// Write graphic control extension (needed for GIF transparancy)
putByte( 0x21, fp); // extension introducer
putByte( 0xf9, fp); // graphic control label
putByte( 4, fp); // block size
putByte( 1, fp); // announce transparacy value
putWord( 0, fp); // zero delay time
putByte( transIndex, fp); // write transparant index
putByte( 0, fp); // end block
}
// Write the image descriptor
putByte( 0x2c, fp); // image separator
putWord( 0, fp); // image left position
putWord( 0, fp); // image top position
putWord( width, fp); // image width
putWord( height, fp); // image height
putByte( 0, fp); // no local color table, no interlacing
// Write table based image data
Byte initCodeSize = depth<=1 ? 2 : depth;
putByte( initCodeSize, fp); // LZW Minimum Code Size
compress( initCodeSize+1, fp);
putByte( 0, fp); // end of blocks
// Write GIF Trailer
putByte( 0x3b, fp);
}
void GifEncoder::compress( int ibits, QFile &outfile )
{
int i;
int entry;
initBits = ibits;
numPixels = width*height;
dataPtr = data;
clearFlag = FALSE;
nBits = initBits;
maxCode = (1<<nBits) -1;
ClearCode = (1 << (initBits - 1));
EOFCode = ClearCode + 1;
freeEntry = ClearCode + 2;
aCount = 0;
curAccum = 0;
curBits = 0;
entry = nextPixel();
int hshift = 0;
int fcode;
for ( fcode = hashTableSize; fcode < 65536L; fcode *= 2L ) ++hshift;
hshift = 8 - hshift; /* set hash code range bound */
clearHashTable(); /* clear hash table */
writeCode( ClearCode,outfile );
int c;
while ( (c = nextPixel()) != EOF )
{
fcode = (c << bits) + entry;
i = (c << hshift) ^ entry; /* xor hashing */
bool found=FALSE;
if (htab[i]==fcode)
{
entry = codetab[i];
found=TRUE;
}
else if (htab[i]>=0)
{
int disp = hashTableSize - i;
if (i==0) disp=1;
do
{
if ((i-=disp)<0) i+=hashTableSize;
if (htab[i]==fcode)
{
entry=codetab[i];
found=TRUE;
}
} while (htab[i]>0 && !found);
}
if (!found)
{
writeCode( entry, outfile );
entry = c;
if ( freeEntry < maxMaxCode )
{
codetab[i] = freeEntry++; /* code -> hashtable */
htab[i] = fcode;
}
else
{
clearHashTable();
freeEntry = ClearCode + 2;
clearFlag = TRUE;
writeCode( ClearCode, outfile );
}
}
}
writeCode( entry, outfile );
writeCode( EOFCode, outfile );
}
void GifEncoder::putWord( Word w, QFile &fp )
{
fp.putch( w & 0xff );
fp.putch( (w>>8) & 0xff );
}
void GifEncoder::putByte( Byte b, QFile &fp )
{
fp.putch( b );
}
void GifEncoder::writeCode( int code, QFile &outfile )
{
curAccum &= masks[ curBits ];
if ( curBits > 0 )
{
curAccum |= (code << curBits);
}
else
{
curAccum = code;
}
curBits += nBits;
while( curBits >= 8 )
{
writeChar( (Byte)(curAccum & 0xff),outfile );
curAccum >>= 8;
curBits -= 8;
}
/*
* If the next entry is going to be too big for the code size,
* then increase it, if possible.
*/
if ( freeEntry > maxCode || clearFlag )
{
if( clearFlag )
{
nBits = initBits;
maxCode = (1<<nBits)-1;
clearFlag = FALSE;
}
else
{
++nBits;
if ( nBits == bits )
maxCode = maxMaxCode;
else
maxCode = (1<<nBits)-1;
}
}
if ( code == EOFCode )
{
/* At EOF, write the rest of the buffer. */
while( curBits > 0 )
{
writeChar( (Byte)(curAccum & 0xff), outfile );
curAccum >>= 8;
curBits -= 8;
}
writePacket(outfile);
}
}
/*
* Add a character to the end of the current packet, and if it is 254
* characters, flush the packet to disk.
*/
void GifEncoder::writeChar( Byte c, QFile &fp )
{
accum[ aCount++ ] = c;
if( aCount >= 254 ) writePacket(fp);
}
/*
* Flush the packet to disk, and reset the accumulator
*/
void GifEncoder::writePacket(QFile &fp)
{
if ( aCount > 0 )
{
fp.putch( aCount );
fp.writeBlock( (const char *)accum, aCount );
aCount = 0;
}
}
void GifEncoder::clearHashTable() /* reset code table */
{
int *htab_p = htab;
int i; for (i=0;i<hashTableSize;i++) *htab_p++ = -1;
}