diff options
Diffstat (limited to 'src/gifenc.cpp')
-rw-r--r-- | src/gifenc.cpp | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/gifenc.cpp b/src/gifenc.cpp new file mode 100644 index 0000000..501e433 --- /dev/null +++ b/src/gifenc.cpp @@ -0,0 +1,287 @@ +/****************************************************************************** + * + * $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; +} + |