com.alee.graphics.image.gif.LZWEncoder Maven / Gradle / Ivy
The newest version!
/*
* This file is part of WebLookAndFeel library.
*
* WebLookAndFeel library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* WebLookAndFeel library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WebLookAndFeel library. If not, see .
*/
package com.alee.graphics.image.gif;
import java.io.IOException;
import java.io.OutputStream;
/**
* Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott.
*
* @author K Weiner 12/00
*/
public class LZWEncoder
{
private static final int EOF = -1;
private final int imgW;
private final int imgH;
private final byte[] pixAry;
private final int initCodeSize;
private int remaining;
private int curPixel;
// GIFCOMPR.C - GIF Image compression routines
//
// Lempel-Ziv compression based on 'compress'. GIF modifications by
// David Rowley ([email protected])
// General DEFINEs
static final int BITS = 12;
static final int HSIZE = 5003; // 80% occupancy
// 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)
int n_bits; // number of bits/code
int maxbits = BITS; // user settable max # bits/code
int maxcode; // maximum code, given n_bits
int maxmaxcode = 1 << BITS; // should NEVER generate this code
int[] htab = new int[ HSIZE ];
int[] codetab = new int[ HSIZE ];
int hsize = HSIZE; // for dynamic table sizing
int free_ent = 0; // first unused entry
// block compression parameters -- after all codes are used up,
// and compression rate changes, start over.
boolean clear_flg = false;
// 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.
int g_init_bits;
int ClearCode;
int EOFCode;
// output
//
// Output the given code.
// Inputs:
// code: A n_bits-bit integer. If == -1, then EOF. This assumes
// that n_bits =< 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.
int cur_accum = 0;
int cur_bits = 0;
int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF,
0x7FFF, 0xFFFF };
// Number of characters so far in this 'packet'
int a_count;
// Define the storage for the packet accumulator
byte[] accum = new byte[ 256 ];
// ----------------------------------------------------------------------------
LZWEncoder ( final int width, final int height, final byte[] pixels, final int color_depth )
{
imgW = width;
imgH = height;
pixAry = pixels;
initCodeSize = Math.max ( 2, color_depth );
}
// Add a character to the end of the current packet, and if it is 254
// characters, flush the packet to disk.
void char_out ( final byte c, final OutputStream outs ) throws IOException
{
accum[ a_count++ ] = c;
if ( a_count >= 254 )
{
flush_char ( outs );
}
}
// Clear out the hash table
// table clear for block compress
void cl_block ( final OutputStream outs ) throws IOException
{
cl_hash ( hsize );
free_ent = ClearCode + 2;
clear_flg = true;
output ( ClearCode, outs );
}
// reset code table
void cl_hash ( final int hsize )
{
for ( int i = 0; i < hsize; ++i )
{
htab[ i ] = -1;
}
}
void compress ( final int init_bits, final OutputStream outs ) throws IOException
{
int fcode;
int i /* = 0 */;
int c;
int ent;
int disp;
final int hsize_reg;
int hshift;
// Set up the globals: g_init_bits - initial number of bits
g_init_bits = init_bits;
// Set up the necessary values
clear_flg = false;
n_bits = g_init_bits;
maxcode = MAXCODE ( n_bits );
ClearCode = 1 << ( init_bits - 1 );
EOFCode = ClearCode + 1;
free_ent = ClearCode + 2;
a_count = 0; // clear packet
ent = nextPixel ();
hshift = 0;
for ( fcode = hsize; fcode < 65536; fcode *= 2 )
{
++hshift;
}
hshift = 8 - hshift; // set hash code range bound
hsize_reg = hsize;
cl_hash ( hsize_reg ); // clear hash table
output ( ClearCode, outs );
outer_loop:
while ( ( c = nextPixel () ) != EOF )
{
fcode = ( c << maxbits ) + ent;
i = ( c << hshift ) ^ ent; // xor hashing
if ( htab[ i ] == fcode )
{
ent = codetab[ i ];
continue;
}
else if ( htab[ i ] >= 0 ) // non-empty slot
{
disp = hsize_reg - i; // secondary hash (after G. Knott)
if ( i == 0 )
{
disp = 1;
}
do
{
if ( ( i -= disp ) < 0 )
{
i += hsize_reg;
}
if ( htab[ i ] == fcode )
{
ent = codetab[ i ];
continue outer_loop;
}
}
while ( htab[ i ] >= 0 );
}
output ( ent, outs );
ent = c;
if ( free_ent < maxmaxcode )
{
codetab[ i ] = free_ent++; // code -> hashtable
htab[ i ] = fcode;
}
else
{
cl_block ( outs );
}
}
// Put out the final code.
output ( ent, outs );
output ( EOFCode, outs );
}
// ----------------------------------------------------------------------------
void encode ( final OutputStream os ) throws IOException
{
os.write ( initCodeSize ); // write "initial code size" byte
remaining = imgW * imgH; // reset navigation variables
curPixel = 0;
compress ( initCodeSize + 1, os ); // compress and write the pixel data
os.write ( 0 ); // write block terminator
}
// Flush the packet to disk, and reset the accumulator
void flush_char ( final OutputStream outs ) throws IOException
{
if ( a_count > 0 )
{
outs.write ( a_count );
outs.write ( accum, 0, a_count );
a_count = 0;
}
}
final int MAXCODE ( final int n_bits )
{
return ( 1 << n_bits ) - 1;
}
// ----------------------------------------------------------------------------
// Return the next pixel from the image
// ----------------------------------------------------------------------------
private int nextPixel ()
{
if ( remaining == 0 )
{
return EOF;
}
--remaining;
final byte pix = pixAry[ curPixel++ ];
return pix & 0xff;
}
void output ( final int code, final OutputStream outs ) throws IOException
{
cur_accum &= masks[ cur_bits ];
if ( cur_bits > 0 )
{
cur_accum |= code << cur_bits;
}
else
{
cur_accum = code;
}
cur_bits += n_bits;
while ( cur_bits >= 8 )
{
char_out ( ( byte ) ( cur_accum & 0xff ), outs );
cur_accum >>= 8;
cur_bits -= 8;
}
// If the next entry is going to be too big for the code size,
// then increase it, if possible.
if ( free_ent > maxcode || clear_flg )
{
if ( clear_flg )
{
maxcode = MAXCODE ( n_bits = g_init_bits );
clear_flg = false;
}
else
{
++n_bits;
if ( n_bits == maxbits )
{
maxcode = maxmaxcode;
}
else
{
maxcode = MAXCODE ( n_bits );
}
}
}
if ( code == EOFCode )
{
// At EOF, write the rest of the buffer.
while ( cur_bits > 0 )
{
char_out ( ( byte ) ( cur_accum & 0xff ), outs );
cur_accum >>= 8;
cur_bits -= 8;
}
flush_char ( outs );
}
}
}