// // Copyright (c) James South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageProcessor.Formats { using System; using System.IO; /// /// Decompresses data using the LZW algorithms. /// internal sealed class LzwDecoder { /// /// One more than the maximum value 12 bit integer. /// private const int MaxStackSize = 4096; /// /// The null code. /// private const int NullCode = -1; /// /// The stream. /// private readonly Stream stream; /// /// Initializes a new instance of the class /// and sets the stream, where the compressed data should be read from. /// /// The stream to read from. /// is null. public LzwDecoder(Stream stream) { Guard.NotNull(stream, nameof(stream)); this.stream = stream; } /// /// Decodes and decompresses all pixel indices from the stream. /// /// /// /// The width of the pixel index array. /// The height of the pixel index array. /// Size of the data. /// The decoded and uncompressed array. 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 code 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; // ÔÚÏÂÃæµÄÑ­»·ÖУ¬Ã¿´Î»á»ñȡһ¶¨Á¿µÄ±àÂëµÄ×Ö½ÚÊý×飬¶ø´¦ÀíÕâÐ(c)Êý×éµÄʱºòÐèÒª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Êý¿É¹(c)´¦Àí£¬ÏÂÃæ¾ÍÊÇ´¦Àí¹ý³Ì // »ñÈ¡±àÂë code = data & codeMask; // »ñÈ¡dataÊý¾ÝµÄ±àÂëλ´óСbitµÄÊý¾Ý data >>= codeSize; // ½«±àÂëÊý¾Ý½ØÈ¡ºó£¬Ô­À´µÄÊý¾Ý¾Íʣϼ¸¸öbitÁË£¬´Ëʱ½«ÕâÐ(c)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; } /// /// 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. /// /// /// The . /// private byte[] ReadBlock() { int blockSize = this.stream.ReadByte(); return this.ReadBytes(blockSize); } /// /// Reads the specified number of bytes from the data stream. /// /// /// The number of bytes to read. /// /// /// The . /// private byte[] ReadBytes(int length) { byte[] buffer = new byte[length]; this.stream.Read(buffer, 0, length); return buffer; } } }