|
|
|
@ -14,6 +14,45 @@ |
|
|
|
private readonly byte[] pixAry; |
|
|
|
|
|
|
|
private readonly int initCodeSize; |
|
|
|
|
|
|
|
// 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.
|
|
|
|
private readonly int[] masks = |
|
|
|
{ |
|
|
|
0x0000, // 0
|
|
|
|
0x0001, // 1
|
|
|
|
0x0003, // 3
|
|
|
|
0x0007, // 7
|
|
|
|
0x000F, // 15
|
|
|
|
0x001F, // 31
|
|
|
|
0x003F, // 63
|
|
|
|
0x007F, // 127
|
|
|
|
0x00FF, // 255
|
|
|
|
0x01FF, // 511
|
|
|
|
0x03FF, // 1023
|
|
|
|
0x07FF, // 2047
|
|
|
|
0x0FFF, // 4095
|
|
|
|
0x1FFF, // 8191
|
|
|
|
0x3FFF, // 16383
|
|
|
|
0x7FFF, // 32767
|
|
|
|
0xFFFF // 65535
|
|
|
|
}; |
|
|
|
|
|
|
|
// Define the storage for the packet accumulator
|
|
|
|
private readonly byte[] accumulatorBytes = new byte[256]; |
|
|
|
|
|
|
|
private int remaining; |
|
|
|
private int curPixel; |
|
|
|
|
|
|
|
@ -49,11 +88,11 @@ |
|
|
|
|
|
|
|
private int hsize = HSIZE; // for dynamic table sizing
|
|
|
|
|
|
|
|
private int free_ent = 0; // first unused entry
|
|
|
|
private int freeEntry = 0; // first unused entry
|
|
|
|
|
|
|
|
// block compression parameters -- after all codes are used up,
|
|
|
|
// and compression rate changes, start over.
|
|
|
|
private bool clear_flg; |
|
|
|
private bool clearFlag; |
|
|
|
|
|
|
|
// Algorithm: use open addressing double hashing (no chaining) on the
|
|
|
|
// prefix code / next character combination. We do a variant of Knuth's
|
|
|
|
@ -72,50 +111,15 @@ |
|
|
|
private int ClearCode; |
|
|
|
private 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.
|
|
|
|
private int currentAccumulator; |
|
|
|
|
|
|
|
private int cur_accum = 0; |
|
|
|
private int cur_bits = 0; |
|
|
|
private int currentBits; |
|
|
|
|
|
|
|
private readonly int[] masks = |
|
|
|
{ |
|
|
|
0x0000, // 0
|
|
|
|
0x0001, // 1
|
|
|
|
0x0003, // 3
|
|
|
|
0x0007, // 7
|
|
|
|
0x000F, // 15
|
|
|
|
0x001F, // 31
|
|
|
|
0x003F, // 63
|
|
|
|
0x007F, // 127
|
|
|
|
0x00FF, // 255
|
|
|
|
0x01FF, // 511
|
|
|
|
0x03FF, // 1023
|
|
|
|
0x07FF, // 2047
|
|
|
|
0x0FFF, // 4095
|
|
|
|
0x1FFF, // 8191
|
|
|
|
0x3FFF, // 16383
|
|
|
|
0x7FFF, // 32767
|
|
|
|
0xFFFF }; // 65535
|
|
|
|
|
|
|
|
// Number of characters so far in this 'packet'
|
|
|
|
/// <summary>
|
|
|
|
/// Number of characters so far in this 'packet'
|
|
|
|
/// </summary>
|
|
|
|
private int accumulatorCount; |
|
|
|
|
|
|
|
// Define the storage for the packet accumulator
|
|
|
|
private readonly byte[] accumulatorBytes = new byte[256]; |
|
|
|
|
|
|
|
public LzwEncoder(int width, int height, byte[] pixels, int colorDepth) |
|
|
|
{ |
|
|
|
this.imgW = width; |
|
|
|
@ -124,13 +128,27 @@ |
|
|
|
this.initCodeSize = Math.Max(2, colorDepth); |
|
|
|
} |
|
|
|
|
|
|
|
public void Encode(Stream stream) |
|
|
|
{ |
|
|
|
stream.WriteByte((byte)this.initCodeSize); // write "initial code size" byte
|
|
|
|
|
|
|
|
this.remaining = this.imgW * this.imgH; // reset navigation variables
|
|
|
|
this.curPixel = 0; |
|
|
|
|
|
|
|
this.Compress(this.initCodeSize + 1, stream); // compress and write the pixel data
|
|
|
|
|
|
|
|
stream.WriteByte(0); // write block terminator
|
|
|
|
} |
|
|
|
|
|
|
|
// Add a character to the end of the current packet, and if it is 254
|
|
|
|
// characters, flush the packet to disk.
|
|
|
|
private void CharOut(byte character, Stream stream) |
|
|
|
{ |
|
|
|
this.accumulatorBytes[this.accumulatorCount++] = character; |
|
|
|
if (this.accumulatorCount >= 254) |
|
|
|
{ |
|
|
|
this.FlushChar(stream); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Clear out the hash table
|
|
|
|
@ -139,8 +157,8 @@ |
|
|
|
private void ClearBlock(Stream outs) |
|
|
|
{ |
|
|
|
this.ClearHashTable(this.hsize); |
|
|
|
this.free_ent = this.ClearCode + 2; |
|
|
|
this.clear_flg = true; |
|
|
|
this.freeEntry = this.ClearCode + 2; |
|
|
|
this.clearFlag = true; |
|
|
|
|
|
|
|
this.Output(this.ClearCode, outs); |
|
|
|
} |
|
|
|
@ -151,14 +169,12 @@ |
|
|
|
for (int i = 0; i < hsize; ++i) |
|
|
|
{ |
|
|
|
this.htab[i] = -1; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void Compress(int init_bits, Stream outs) |
|
|
|
{ |
|
|
|
int fcode; |
|
|
|
int i /* = 0 */; |
|
|
|
int c; |
|
|
|
int ent; |
|
|
|
int disp; |
|
|
|
@ -169,13 +185,13 @@ |
|
|
|
this.globalInitialBits = init_bits; |
|
|
|
|
|
|
|
// Set up the necessary values
|
|
|
|
this.clear_flg = false; |
|
|
|
this.clearFlag = false; |
|
|
|
this.numberOfBits = this.globalInitialBits; |
|
|
|
this.maxcode = this.MAXCODE(this.numberOfBits); |
|
|
|
|
|
|
|
this.ClearCode = 1 << (init_bits - 1); |
|
|
|
this.EOFCode = this.ClearCode + 1; |
|
|
|
this.free_ent = this.ClearCode + 2; |
|
|
|
this.freeEntry = this.ClearCode + 2; |
|
|
|
|
|
|
|
this.accumulatorCount = 0; // clear packet
|
|
|
|
|
|
|
|
@ -183,7 +199,10 @@ |
|
|
|
|
|
|
|
hshift = 0; |
|
|
|
for (fcode = this.hsize; fcode < 65536; fcode *= 2) |
|
|
|
{ |
|
|
|
++hshift; |
|
|
|
} |
|
|
|
|
|
|
|
hshift = 8 - hshift; // set hash code range bound
|
|
|
|
|
|
|
|
hsize_reg = this.hsize; |
|
|
|
@ -196,7 +215,7 @@ |
|
|
|
while ((c = this.NextPixel()) != EOF) |
|
|
|
{ |
|
|
|
fcode = (c << this.maxbits) + ent; |
|
|
|
i = c << hshift ^ ent; // xor hashing
|
|
|
|
int i = c << hshift ^ ent; |
|
|
|
|
|
|
|
if (this.htab[i] == fcode) |
|
|
|
{ |
|
|
|
@ -204,29 +223,37 @@ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.htab[i] >= 0) // non-empty slot
|
|
|
|
// non-empty slot
|
|
|
|
if (this.htab[i] >= 0) |
|
|
|
{ |
|
|
|
disp = hsize_reg - i; // secondary hash (after G. Knott)
|
|
|
|
if (i == 0) disp = 1; |
|
|
|
if (i == 0) |
|
|
|
{ |
|
|
|
disp = 1; |
|
|
|
} |
|
|
|
|
|
|
|
do |
|
|
|
{ |
|
|
|
if ((i -= disp) < 0) i += hsize_reg; |
|
|
|
if ((i -= disp) < 0) |
|
|
|
{ |
|
|
|
i += hsize_reg; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.htab[i] == fcode) |
|
|
|
{ |
|
|
|
ent = this.codetab[i]; |
|
|
|
goto outer_loop; |
|
|
|
} |
|
|
|
|
|
|
|
} while (this.htab[i] >= 0); |
|
|
|
} |
|
|
|
while (this.htab[i] >= 0); |
|
|
|
} |
|
|
|
|
|
|
|
this.Output(ent, outs); |
|
|
|
ent = c; |
|
|
|
|
|
|
|
if (this.free_ent < this.maxmaxcode) |
|
|
|
if (this.freeEntry < this.maxmaxcode) |
|
|
|
{ |
|
|
|
this.codetab[i] = this.free_ent++; // code -> hashtable
|
|
|
|
this.codetab[i] = this.freeEntry++; // code -> hashtable
|
|
|
|
this.htab[i] = fcode; |
|
|
|
} |
|
|
|
else |
|
|
|
@ -240,19 +267,6 @@ |
|
|
|
this.Output(this.EOFCode, outs); |
|
|
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
public void Encode(Stream stream) |
|
|
|
{ |
|
|
|
stream.WriteByte((byte)this.initCodeSize); // write "initial code size" byte
|
|
|
|
|
|
|
|
this.remaining = this.imgW * this.imgH; // reset navigation variables
|
|
|
|
this.curPixel = 0; |
|
|
|
|
|
|
|
this.Compress(this.initCodeSize + 1, stream); // compress and write the pixel data
|
|
|
|
|
|
|
|
stream.WriteByte(0); // write block terminator
|
|
|
|
} |
|
|
|
|
|
|
|
// Flush the packet to disk, and reset the accumulator
|
|
|
|
private void FlushChar(Stream stream) |
|
|
|
{ |
|
|
|
@ -275,7 +289,9 @@ |
|
|
|
private int NextPixel() |
|
|
|
{ |
|
|
|
if (this.remaining == 0) |
|
|
|
{ |
|
|
|
return EOF; |
|
|
|
} |
|
|
|
|
|
|
|
--this.remaining; |
|
|
|
|
|
|
|
@ -284,30 +300,36 @@ |
|
|
|
return pix & 0xff; |
|
|
|
} |
|
|
|
|
|
|
|
void Output(int code, Stream outs) |
|
|
|
private void Output(int code, Stream outs) |
|
|
|
{ |
|
|
|
this.cur_accum &= this.masks[this.cur_bits]; |
|
|
|
this.currentAccumulator &= this.masks[this.currentBits]; |
|
|
|
|
|
|
|
if (this.cur_bits > 0) this.cur_accum |= (code << this.cur_bits); |
|
|
|
else this.cur_accum = code; |
|
|
|
if (this.currentBits > 0) |
|
|
|
{ |
|
|
|
this.currentAccumulator |= code << this.currentBits; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
this.currentAccumulator = code; |
|
|
|
} |
|
|
|
|
|
|
|
this.cur_bits += this.numberOfBits; |
|
|
|
this.currentBits += this.numberOfBits; |
|
|
|
|
|
|
|
while (this.cur_bits >= 8) |
|
|
|
while (this.currentBits >= 8) |
|
|
|
{ |
|
|
|
this.CharOut((byte)(this.cur_accum & 0xff), outs); |
|
|
|
this.cur_accum >>= 8; |
|
|
|
this.cur_bits -= 8; |
|
|
|
this.CharOut((byte)(this.currentAccumulator & 0xff), outs); |
|
|
|
this.currentAccumulator >>= 8; |
|
|
|
this.currentBits -= 8; |
|
|
|
} |
|
|
|
|
|
|
|
// If the next entry is going to be too big for the code size,
|
|
|
|
// then increase it, if possible.
|
|
|
|
if (this.free_ent > this.maxcode || this.clear_flg) |
|
|
|
if (this.freeEntry > this.maxcode || this.clearFlag) |
|
|
|
{ |
|
|
|
if (this.clear_flg) |
|
|
|
if (this.clearFlag) |
|
|
|
{ |
|
|
|
this.maxcode = this.MAXCODE(this.numberOfBits = this.globalInitialBits); |
|
|
|
this.clear_flg = false; |
|
|
|
this.clearFlag = false; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
@ -321,11 +343,11 @@ |
|
|
|
if (code == this.EOFCode) |
|
|
|
{ |
|
|
|
// At EOF, write the rest of the buffer.
|
|
|
|
while (this.cur_bits > 0) |
|
|
|
while (this.currentBits > 0) |
|
|
|
{ |
|
|
|
this.CharOut((byte)(this.cur_accum & 0xff), outs); |
|
|
|
this.cur_accum >>= 8; |
|
|
|
this.cur_bits -= 8; |
|
|
|
this.CharOut((byte)(this.currentAccumulator & 0xff), outs); |
|
|
|
this.currentAccumulator >>= 8; |
|
|
|
this.currentBits -= 8; |
|
|
|
} |
|
|
|
|
|
|
|
this.FlushChar(outs); |
|
|
|
|