mirror of https://github.com/SixLabors/ImageSharp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
12 KiB
267 lines
12 KiB
// <copyright file="LzwDecoder.cs" company="James South">
|
|
// Copyright © James South and contributors.
|
|
// Licensed under the Apache License, Version 2.0.
|
|
// </copyright>
|
|
|
|
namespace ImageProcessor.Formats
|
|
{
|
|
using System;
|
|
using System.IO;
|
|
|
|
/// <summary>
|
|
/// Decompresses data using the LZW algorithms.
|
|
/// </summary>
|
|
internal sealed class LzwDecoder
|
|
{
|
|
/// <summary>
|
|
/// One more than the maximum value 12 bit integer.
|
|
/// </summary>
|
|
private const int MaxStackSize = 4096;
|
|
|
|
/// <summary>
|
|
/// The null code.
|
|
/// </summary>
|
|
private const int NullCode = -1;
|
|
|
|
/// <summary>
|
|
/// The stream.
|
|
/// </summary>
|
|
private readonly Stream stream;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="LzwDecoder"/> class
|
|
/// and sets the stream, where the compressed data should be read from.
|
|
/// </summary>
|
|
/// <param name="stream">The stream to read from.</param>
|
|
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception>
|
|
public LzwDecoder(Stream stream)
|
|
{
|
|
Guard.NotNull(stream, nameof(stream));
|
|
|
|
this.stream = stream;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decodes and decompresses all pixel indices from the stream.
|
|
/// <remarks>
|
|
/// </remarks>
|
|
/// </summary>
|
|
/// <param name="width">The width of the pixel index array.</param>
|
|
/// <param name="height">The height of the pixel index array.</param>
|
|
/// <param name="dataSize">Size of the data.</param>
|
|
/// <returns>The decoded and uncompressed array.</returns>
|
|
public byte[] DecodePixels(int width, int height, int dataSize)
|
|
{
|
|
Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize));
|
|
|
|
// The resulting index table.
|
|
byte[] pixels = new byte[width * height];
|
|
|
|
// Calculate the clear code. The value of the clear code is 2 ^ dataSize
|
|
int clearCode = 1 << dataSize;
|
|
|
|
int codeSize = dataSize + 1;
|
|
|
|
// Calculate the end code
|
|
int endCode = clearCode + 1;
|
|
|
|
// Calculate the available code.
|
|
int availableCode = clearCode + 2;
|
|
|
|
// Jillzhangs Code (Not From Me) see: http://giflib.codeplex.com/
|
|
// TODO: It's imperative that this close is cleaned up and commented properly.
|
|
// TODO: Unfortunately I can't figure out the character encoding to translate from the original Chinese.
|
|
int code; // ÓÃÓÚ´æ´¢µ±Ç°µÄ±àÂëÖµ
|
|
int oldCode = NullCode; // ÓÃÓÚ´æ´¢ÉÏÒ»´ÎµÄ±àÂëÖµ
|
|
int codeMask = (1 << codeSize) - 1; // ±íʾ±àÂëµÄ×î´óÖµ£¬Èç¹ûcodeSize=5,Ôòcode_mask=31
|
|
int bits = 0; // ÔÚ±àÂëÁ÷ÖÐÊý¾ÝµÄ±£´æÐÎʽΪbyte£¬¶øÊµ¼Ê±àÂë¹ý³ÌÖÐÊÇÕÒʵ¼Ê±àÂëλÀ´´æ´¢µÄ£¬±ÈÈçµ±codeSize=5µÄʱºò£¬ÄÇôʵ¼ÊÉÏ5bitµÄÊý¾Ý¾ÍÓ¦¸Ã¿ÉÒÔ±íʾһ¸ö±àÂ룬ÕâÑùÈ¡³öÀ´µÄ1¸ö×ֽھ͸»ÓàÁË3¸öbit£¬Õâ3¸öbitÓÃÓں͵ڶþ¸ö×ֽڵĺóÁ½¸öbit½øÐÐ×éºÏ£¬ÔÙ´ÎÐγɱàÂëÖµ£¬Èç´ËÀàÍÆ
|
|
|
|
int[] prefix = new int[MaxStackSize]; // ÓÃÓÚ±£´æÇ°×ºµÄ¼¯ºÏ
|
|
int[] suffix = new int[MaxStackSize]; // ÓÃÓÚ±£´æºó׺
|
|
int[] pixelStatck = new int[MaxStackSize + 1]; // ÓÃÓÚÁÙʱ±£´æÊý¾ÝÁ÷
|
|
|
|
int top = 0;
|
|
int count = 0; // ÔÚÏÂÃæµÄÑ»·ÖУ¬Ã¿´Î»á»ñȡһ¶¨Á¿µÄ±àÂëµÄ×Ö½ÚÊý×飬¶ø´¦ÀíÕâЩÊý×éµÄʱºòÐèÒª1¸ö¸ö×Ö½ÚÀ´´¦Àí£¬count¾ÍÊDZíʾ»¹Òª´¦ÀíµÄ×Ö½ÚÊýÄ¿
|
|
int bi = 0; // count±íʾ»¹Ê£¶àÉÙ×Ö½ÚÐèÒª´¦Àí£¬¶øbiÔò±íʾ±¾´ÎÒѾ´¦ÀíµÄ¸öÊý
|
|
int xyz = 0; // i´ú±íµ±Ç°´¦ÀíµÃµ½ÏñËØÊý
|
|
|
|
int data = 0; // ±íʾµ±Ç°´¦ÀíµÄÊý¾ÝµÄÖµ
|
|
int first = 0; // Ò»¸ö×Ö·û´®ÖصĵÚÒ»¸ö×Ö½Ú
|
|
|
|
// ÏÈÉú³ÉÔªÊý¾ÝµÄǰ׺¼¯ºÏºÍºó׺¼¯ºÏ£¬ÔªÊý¾ÝµÄǰ׺¾ùΪ0£¬¶øºó׺ÓëÔªÊý¾ÝÏàµÈ£¬Í¬Ê±±àÂëÒ²ÓëÔªÊý¾ÝÏàµÈ
|
|
for (code = 0; code < clearCode; code++)
|
|
{
|
|
// ǰ׺³õʼΪ0
|
|
prefix[code] = 0;
|
|
|
|
// ºó׺=ÔªÊý¾Ý=±àÂë
|
|
suffix[code] = (byte)code;
|
|
}
|
|
|
|
byte[] buffer = null;
|
|
while (xyz < pixels.Length)
|
|
{
|
|
// ×î´óÏñËØÊýÒѾȷ¶¨ÎªpixelCount = width * width
|
|
if (top == 0)
|
|
{
|
|
if (bits < codeSize)
|
|
{
|
|
// Èç¹ûµ±Ç°µÄÒª´¦ÀíµÄbitÊýСÓÚ±àÂëλ´óС£¬ÔòÐèÒª¼ÓÔØÊý¾Ý
|
|
if (count == 0)
|
|
{
|
|
// Èç¹ûcountΪ0£¬±íʾҪ´Ó±àÂëÁ÷ÖжÁÒ»¸öÊý¾Ý¶ÎÀ´½øÐзÖÎö
|
|
buffer = this.ReadBlock();
|
|
count = buffer.Length;
|
|
if (count == 0)
|
|
{
|
|
// ÔÙ´ÎÏë¶ÁÈ¡Êý¾Ý¶Î£¬È´Ã»ÓжÁµ½Êý¾Ý£¬´Ëʱ¾Í±íÃ÷ÒѾ´¦ÀíÍêÁË
|
|
break;
|
|
}
|
|
|
|
// ÖØÐ¶Áȡһ¸öÊý¾Ý¶Îºó£¬Ó¦¸Ã½«ÒѾ´¦ÀíµÄ¸öÊýÖÃ0
|
|
bi = 0;
|
|
}
|
|
|
|
// »ñÈ¡±¾´ÎÒª´¦ÀíµÄÊý¾ÝµÄÖµ
|
|
if (buffer != null)
|
|
{
|
|
data += buffer[bi] << bits; // ´Ë´¦ÎªºÎÒªÒÆÎ»ÄØ£¬±ÈÈçµÚÒ»´Î´¦ÀíÁË1¸ö×Ö½ÚΪ176£¬µÚÒ»´ÎÖ»Òª´¦Àí5bit¾Í¹»ÁË£¬Ê£ÏÂ3bitÁô¸øÏ¸ö×Ö½Ú½øÐÐ×éºÏ¡£Ò²¾ÍÊǵڶþ¸ö×ֽڵĺóÁ½Î»+µÚÒ»¸ö×Ö½ÚµÄǰÈýλ×é³ÉµÚ¶þ´ÎÊä³öÖµ
|
|
}
|
|
|
|
bits += 8; // ±¾´ÎÓÖ´¦ÀíÁËÒ»¸ö×Ö½Ú£¬ËùÒÔÐèÒª+8
|
|
bi++; // ½«´¦ÀíÏÂÒ»¸ö×Ö½Ú
|
|
count--; // ÒѾ´¦Àí¹ýµÄ×Ö½ÚÊý+1
|
|
continue;
|
|
}
|
|
|
|
// Èç¹ûÒѾÓÐ×ã¹»µÄbitÊý¿É¹©´¦Àí£¬ÏÂÃæ¾ÍÊÇ´¦Àí¹ý³Ì
|
|
// »ñÈ¡±àÂë
|
|
code = data & codeMask; // »ñÈ¡dataÊý¾ÝµÄ±àÂëλ´óСbitµÄÊý¾Ý
|
|
data >>= codeSize; // ½«±àÂëÊý¾Ý½ØÈ¡ºó£¬ÔÀ´µÄÊý¾Ý¾Íʣϼ¸¸öbitÁË£¬´Ëʱ½«ÕâЩbitÓÒÒÆ£¬ÎªÏ´Î×÷×¼±¸
|
|
bits -= codeSize; // ͬʱÐèÒª½«µ±Ç°Êý¾ÝµÄbitÊý¼õÈ¥±àÂë볤£¬ÒòΪÒѾµÃµ½ÁË´¦Àí¡£
|
|
|
|
// ÏÂÃæ¸ù¾Ý»ñÈ¡µÄcodeÖµÀ´½øÐд¦Àí
|
|
if (code > availableCode || code == endCode)
|
|
{
|
|
// µ±±àÂëÖµ´óÓÚ×î´ó±àÂëÖµ»òÕßΪ½áÊø±ê¼ÇµÄʱºò£¬Í£Ö¹´¦Àí
|
|
break;
|
|
}
|
|
|
|
if (code == clearCode)
|
|
{
|
|
// Èç¹ûµ±Ç°ÊÇÇå³ý±ê¼Ç£¬ÔòÖØÐ³õʼ»¯±äÁ¿£¬ºÃÖØÐÂÔÙÀ´
|
|
codeSize = dataSize + 1;
|
|
|
|
// ÖØÐ³õʼ»¯×î´ó±àÂëÖµ
|
|
codeMask = (1 << codeSize) - 1;
|
|
|
|
// ³õʼ»¯ÏÂÒ»²½Ó¦¸Ã´¦ÀíµÃ±àÂëÖµ
|
|
availableCode = clearCode + 2;
|
|
|
|
// ½«±£´æµ½old_codeÖеÄÖµÇå³ý£¬ÒÔ±ãÖØÍ·ÔÙÀ´
|
|
oldCode = NullCode;
|
|
continue;
|
|
}
|
|
|
|
// ÏÂÃæÊÇcodeÊôÓÚÄÜѹËõµÄ±àÂ뷶ΧÄڵĵĴ¦Àí¹ý³Ì
|
|
if (oldCode == NullCode)
|
|
{
|
|
// Èç¹ûµ±Ç°±àÂëֵΪ¿Õ,±íʾÊǵÚÒ»´Î»ñÈ¡±àÂë
|
|
pixelStatck[top++] = suffix[code]; // »ñÈ¡µ½1¸öÊý¾ÝÁ÷µÄÊý¾Ý
|
|
|
|
// ±¾´Î±àÂë´¦ÀíÍê³É£¬½«±àÂëÖµ±£´æµ½old_codeÖÐ
|
|
oldCode = code;
|
|
|
|
// µÚÒ»¸ö×Ö·ûΪµ±Ç°±àÂë
|
|
first = code;
|
|
continue;
|
|
}
|
|
|
|
int inCode = code; // ÔÚlzwÖУ¬Èç¹ûÈÏʶÁËÒ»¸ö±àÂëËù´ú±íµÄÊý¾Ýentry£¬Ôò½«±àÂë×÷ΪÏÂÒ»´ÎµÄprefix£¬´Ë´¦inCode´ú±í´«µÝ¸øÏÂÒ»´Î×÷Ϊǰ׺µÄ±àÂëÖµ
|
|
if (code == availableCode)
|
|
{
|
|
// Èç¹ûµ±Ç°±àÂëºÍ±¾´ÎÓ¦¸ÃÉú³ÉµÄ±àÂëÏàͬ
|
|
// ÄÇôÏÂÒ»¸öÊý¾Ý×ֽھ͵ÈÓÚµ±Ç°´¦Àí×Ö·û´®µÄµÚÒ»¸ö×Ö½Ú
|
|
pixelStatck[top++] = (byte)first;
|
|
|
|
code = oldCode; // »ØËݵ½ÉÏÒ»¸ö±àÂë
|
|
}
|
|
|
|
while (code > clearCode)
|
|
{
|
|
// Èç¹ûµ±Ç°±àÂë´óÓÚÇå³ý±ê¼Ç£¬±íʾ±àÂëÖµÊÇÄÜѹËõÊý¾ÝµÄ
|
|
pixelStatck[top++] = suffix[code];
|
|
code = prefix[code]; // »ØËݵ½ÉÏÒ»¸ö±àÂë
|
|
}
|
|
|
|
first = suffix[code];
|
|
|
|
// »ñÈ¡ÏÂÒ»¸öÊý¾Ý
|
|
pixelStatck[top++] = suffix[code];
|
|
|
|
// Fix for Gifs that have "deferred clear code" as per here :
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=55918
|
|
if (availableCode < MaxStackSize)
|
|
{
|
|
// ÉèÖõ±Ç°Ó¦¸Ã±àÂëλÖõÄǰ׺
|
|
prefix[availableCode] = oldCode;
|
|
|
|
// ÉèÖõ±Ç°Ó¦¸Ã±àÂëλÖõĺó׺
|
|
suffix[availableCode] = first;
|
|
|
|
// Ï´ÎÓ¦¸ÃµÃµ½µÄ±àÂëÖµ
|
|
availableCode++;
|
|
if (availableCode == codeMask + 1 && availableCode < MaxStackSize)
|
|
{
|
|
// Ôö¼Ó±àÂëλÊý
|
|
codeSize++;
|
|
|
|
// ÖØÉè×î´ó±àÂëÖµ
|
|
codeMask = (1 << codeSize) - 1;
|
|
}
|
|
}
|
|
|
|
// »¹Ôold_code
|
|
oldCode = inCode;
|
|
}
|
|
|
|
// »ØËݵ½ÉÏÒ»¸ö´¦ÀíλÖÃ
|
|
top--;
|
|
|
|
// »ñȡԪÊý¾Ý
|
|
pixels[xyz++] = (byte)pixelStatck[top];
|
|
}
|
|
|
|
return pixels;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads the next data block from the stream. A data block begins with a byte,
|
|
/// which defines the size of the block, followed by the block itself.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// The <see cref="T:byte[]"/>.
|
|
/// </returns>
|
|
private byte[] ReadBlock()
|
|
{
|
|
int blockSize = this.stream.ReadByte();
|
|
return this.ReadBytes(blockSize);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads the specified number of bytes from the data stream.
|
|
/// </summary>
|
|
/// <param name="length">
|
|
/// The number of bytes to read.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The <see cref="T:byte[]"/>.
|
|
/// </returns>
|
|
private byte[] ReadBytes(int length)
|
|
{
|
|
byte[] buffer = new byte[length];
|
|
this.stream.Read(buffer, 0, length);
|
|
return buffer;
|
|
}
|
|
}
|
|
}
|
|
|