mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Fix #359 Former-commit-id: e140a2e3bb28ec1332e0d4caf0de1eaeed154376 Former-commit-id: db6a61bfbe42c744ef7ad47444019a77a9d439dc Former-commit-id: 88b7684fa215b5e7641a8f012fef6b97313a6935af/merge-core
5 changed files with 415 additions and 697 deletions
@ -1,432 +0,0 @@ |
|||
namespace ImageProcessorCore.Formats |
|||
{ |
|||
using System; |
|||
using System.IO; |
|||
|
|||
/// <summary>
|
|||
/// An implementation of the Lempel-Ziv-Welch lossless compression algorithm.
|
|||
/// See http://en.wikipedia.org/wiki/Lzw
|
|||
/// </summary>
|
|||
public class LzwEncoder2 |
|||
{ |
|||
#region declarations
|
|||
private const int _EOF = -1; |
|||
private const int _BITS = 12; |
|||
private const int _HSIZE = 5003; // 80% occupancy
|
|||
|
|||
/// <summary>
|
|||
/// A collection of indices within the active colour table of the
|
|||
/// colours of the pixels within the image.
|
|||
/// </summary>
|
|||
private byte[] _pixels; |
|||
private int _initCodeSize; |
|||
|
|||
/// <summary>
|
|||
/// Number of pixels still to process.
|
|||
/// </summary>
|
|||
private int _pixelsRemaining; |
|||
|
|||
/// <summary>
|
|||
/// Index of the current position within the IndexedPixels collection.
|
|||
/// </summary>
|
|||
private int _pixelIndex; |
|||
|
|||
/// <summary>
|
|||
/// Number of bits per encoded code.
|
|||
/// </summary>
|
|||
int _codeSize; // number of bits/code
|
|||
|
|||
/// <summary>
|
|||
/// Maximum number of bits per encoded code.
|
|||
/// </summary>
|
|||
int _maxCodeSize = _BITS; // user settable max # bits/code
|
|||
|
|||
/// <summary>
|
|||
/// The largest possible code given the current value of _codeSize.
|
|||
/// </summary>
|
|||
int _maxCode; // maximum code, given n_bits
|
|||
|
|||
/// <summary>
|
|||
/// The largest possible code given the largest possible value of
|
|||
/// _codeSize, plus 1. We should never output this code.
|
|||
/// </summary>
|
|||
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
|
|||
|
|||
/// <summary>
|
|||
/// The next unused code. Initially set to the clear code plus 2.
|
|||
/// </summary>
|
|||
int _nextAvailableCode; // first unused entry
|
|||
|
|||
// block compression parameters -- after all codes are used up,
|
|||
// and compression rate changes, start over.
|
|||
bool _clear_flg; |
|||
|
|||
int _g_init_bits; |
|||
|
|||
/// <summary>
|
|||
/// Clear code. This is written out by the encoder when the dictionary
|
|||
/// is full, and is an instruction to the decoder to empty its dictionary
|
|||
/// </summary>
|
|||
int _clearCode; |
|||
|
|||
/// <summary>
|
|||
/// End of information code. This is set to the clear code plus 1 and
|
|||
/// marks the end of the encoded data.
|
|||
/// </summary>
|
|||
int _endOfInformationCode; |
|||
|
|||
int _cur_accum; |
|||
int _cur_bits; |
|||
|
|||
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'
|
|||
/// <summary>
|
|||
/// Number of bytes that have been added to the packet so far.
|
|||
/// </summary>
|
|||
int _byteCountInPacket; |
|||
|
|||
// Define the storage for the packet accumulator
|
|||
/// <summary>
|
|||
/// An array of encoded bytes which are waiting to be written to the
|
|||
/// output stream. The bytes are written out once 254 of them have
|
|||
/// been populated.
|
|||
/// </summary>
|
|||
byte[] _packet = new byte[256]; |
|||
#endregion
|
|||
|
|||
#region constructor
|
|||
/// <summary>
|
|||
/// Constructor.
|
|||
/// </summary>
|
|||
/// <param name="pixels">
|
|||
/// Indices in the active colour table of the colours of the pixel
|
|||
/// making up the image.
|
|||
/// </param>
|
|||
/// <exception cref="ArgumentNullException">
|
|||
/// The supplied pixel collection is null.
|
|||
/// </exception>
|
|||
public LzwEncoder2(byte[] pixels) |
|||
{ |
|||
if (pixels == null) |
|||
{ |
|||
throw new ArgumentNullException("pixels"); |
|||
} |
|||
|
|||
_pixels = pixels; |
|||
// _initCodeSize = Math.Max(2, colourDepth);
|
|||
_initCodeSize = 8; // only seems to work reliably when 8, even if this is sometimes larger than needed
|
|||
} |
|||
#endregion
|
|||
|
|||
#region Encode method
|
|||
/// <summary>
|
|||
/// Encodes the data and writes it to the supplied output stream.
|
|||
/// </summary>
|
|||
/// <param name="outputStream">Output stream</param>
|
|||
/// <exception cref="ArgumentNullException">
|
|||
/// The supplied output stream is null.
|
|||
/// </exception>
|
|||
public void Encode(Stream outputStream) |
|||
{ |
|||
if (outputStream == null) |
|||
{ |
|||
throw new ArgumentNullException("outputStream"); |
|||
} |
|||
|
|||
outputStream.WriteByte(Convert.ToByte(_initCodeSize)); // write "initial code size" byte
|
|||
|
|||
_pixelsRemaining = _pixels.Length; |
|||
_pixelIndex = 0; |
|||
|
|||
Compress(_initCodeSize + 1, outputStream); // compress and write the pixel data
|
|||
|
|||
outputStream.WriteByte(0); // write block terminator
|
|||
} |
|||
#endregion
|
|||
|
|||
#region private methods
|
|||
|
|||
#region private Add method
|
|||
/// <summary>
|
|||
/// Add a character to the end of the current packet, and if the packet
|
|||
/// is 254 characters long, flush the packet to disk.
|
|||
/// </summary>
|
|||
/// <param name="c">The character to add</param>
|
|||
/// <param name="outputStream">Output stream</param>
|
|||
private void Add(byte c, Stream outputStream) |
|||
{ |
|||
_packet[_byteCountInPacket++] = c; |
|||
if (_byteCountInPacket >= 254) |
|||
{ |
|||
Flush(outputStream); |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
#region private ClearTable method
|
|||
/// <summary>
|
|||
/// Clears out the hash table.
|
|||
/// </summary>
|
|||
/// <param name="outs">Output stream</param>
|
|||
private void ClearTable(Stream outs) |
|||
{ |
|||
ResetCodeTable(_hsize); |
|||
_nextAvailableCode = _clearCode + 2; |
|||
_clear_flg = true; |
|||
|
|||
Output(_clearCode, outs); |
|||
} |
|||
#endregion
|
|||
|
|||
#region private ResetCodeTable method
|
|||
/// <summary>
|
|||
/// Resets the code table
|
|||
/// </summary>
|
|||
/// <param name="hsize"></param>
|
|||
private void ResetCodeTable(int hsize) |
|||
{ |
|||
for (int i = 0; i < hsize; ++i) |
|||
{ |
|||
_htab[i] = -1; |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
#region private Compress method
|
|||
/// <summary>
|
|||
/// Compress method
|
|||
/// </summary>
|
|||
/// <param name="init_bits"></param>
|
|||
/// <param name="outs"></param>
|
|||
private void Compress(int init_bits, Stream outs) |
|||
{ |
|||
int fcode; |
|||
int i /* = 0 */; |
|||
int c; |
|||
int ent; |
|||
int disp; |
|||
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; |
|||
_codeSize = _g_init_bits; |
|||
_maxCode = MaxCode(_codeSize); |
|||
|
|||
_clearCode = 1 << (init_bits - 1); |
|||
_endOfInformationCode = _clearCode + 1; |
|||
_nextAvailableCode = _clearCode + 2; |
|||
|
|||
_byteCountInPacket = 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; |
|||
ResetCodeTable(hsize_reg); // clear hash table
|
|||
|
|||
Output(_clearCode, outs); |
|||
|
|||
outer_loop: while ((c = NextPixel()) != _EOF) |
|||
{ |
|||
fcode = (c << _maxCodeSize) + 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]; |
|||
goto outer_loop; |
|||
} |
|||
} while (_htab[i] >= 0); |
|||
} |
|||
Output(ent, outs); |
|||
ent = c; |
|||
if (_nextAvailableCode < _maxMaxCode) |
|||
{ |
|||
_codetab[i] = _nextAvailableCode++; // code -> hashtable
|
|||
_htab[i] = fcode; |
|||
} |
|||
else |
|||
ClearTable(outs); |
|||
} |
|||
// Put out the final code.
|
|||
Output(ent, outs); |
|||
Output(_endOfInformationCode, outs); |
|||
} |
|||
#endregion
|
|||
|
|||
#region private Flush method
|
|||
/// <summary>
|
|||
/// Flush the packet to disk, and reset the accumulator
|
|||
/// </summary>
|
|||
/// <param name="outs"></param>
|
|||
private void Flush(Stream outs) |
|||
{ |
|||
if (_byteCountInPacket > 0) |
|||
{ |
|||
outs.WriteByte(Convert.ToByte(_byteCountInPacket)); |
|||
outs.Write(_packet, 0, _byteCountInPacket); |
|||
_byteCountInPacket = 0; |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
#region private static MaxCode method
|
|||
/// <summary>
|
|||
/// Calculates and returns the maximum possible code given the supplied
|
|||
/// code size.
|
|||
/// This is calculated as 2 to the power of the code size, minus one.
|
|||
/// </summary>
|
|||
/// <param name="codeSize">
|
|||
/// Code size in bits.
|
|||
/// </param>
|
|||
/// <returns></returns>
|
|||
private static int MaxCode(int codeSize) |
|||
{ |
|||
return (1 << codeSize) - 1; |
|||
} |
|||
#endregion
|
|||
|
|||
#region private NextPixel method
|
|||
/// <summary>
|
|||
/// Gets the next pixel from the supplied IndexedPixels collection,
|
|||
/// increments the index of the current position within the collection,
|
|||
/// and decrements the number of pixels remaining.
|
|||
/// </summary>
|
|||
/// <returns></returns>
|
|||
private int NextPixel() |
|||
{ |
|||
if (_pixelsRemaining == 0) |
|||
{ |
|||
// We've processed all the supplied pixel indices so return an
|
|||
// end of file indicator.
|
|||
return _EOF; |
|||
} |
|||
|
|||
--_pixelsRemaining; |
|||
|
|||
byte pix = _pixels[_pixelIndex++]; |
|||
|
|||
return pix; |
|||
} |
|||
#endregion
|
|||
|
|||
#region private Output method
|
|||
/// <summary>
|
|||
/// Adds an encoded LZW code to a buffer ready to be written to the
|
|||
/// output stream. Any full bytes contained in the buffer are then
|
|||
/// written to the output stream and removed from the buffer.
|
|||
/// </summary>
|
|||
/// <param name="code"></param>
|
|||
/// <param name="outputStream">
|
|||
/// The output stream to write to.
|
|||
/// </param>
|
|||
private void Output(int code, Stream outputStream) |
|||
{ |
|||
_cur_accum &= _masks[_cur_bits]; |
|||
|
|||
if (_cur_bits > 0) |
|||
{ |
|||
_cur_accum |= (code << _cur_bits); |
|||
} |
|||
else |
|||
{ |
|||
_cur_accum = code; |
|||
} |
|||
|
|||
_cur_bits += _codeSize; |
|||
|
|||
while (_cur_bits >= 8) |
|||
{ |
|||
Add((byte)(_cur_accum & 0xff), outputStream); |
|||
_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 (_nextAvailableCode > _maxCode || _clear_flg) |
|||
{ |
|||
if (_clear_flg) |
|||
{ |
|||
_maxCode = MaxCode(_codeSize = _g_init_bits); |
|||
_clear_flg = false; |
|||
} |
|||
else |
|||
{ |
|||
++_codeSize; |
|||
if (_codeSize == _maxCodeSize) |
|||
{ |
|||
_maxCode = _maxMaxCode; |
|||
} |
|||
else |
|||
{ |
|||
_maxCode = MaxCode(_codeSize); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (code == _endOfInformationCode) |
|||
{ |
|||
// At EOF, write the rest of the buffer.
|
|||
while (_cur_bits > 0) |
|||
{ |
|||
Add((byte)(_cur_accum & 0xff), outputStream); |
|||
_cur_accum >>= 8; |
|||
_cur_bits -= 8; |
|||
} |
|||
|
|||
Flush(outputStream); |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
Loading…
Reference in new issue