Пример #1
0
        /// <summary>
        /// Reads the transformations, if any are present.
        /// </summary>
        /// <param name="xSize">The width of the image.</param>
        /// <param name="ySize">The height of the image.</param>
        /// <param name="decoder">Vp8LDecoder where the transformations will be stored.</param>
        private void ReadTransformation(int xSize, int ySize, Vp8LDecoder decoder)
        {
            var transformType = (Vp8LTransformType)this.bitReader.ReadValue(2);
            var transform     = new Vp8LTransform(transformType, xSize, ySize);

            // Each transform is allowed to be used only once.
            foreach (Vp8LTransform decoderTransform in decoder.Transforms)
            {
                if (decoderTransform.TransformType == transform.TransformType)
                {
                    WebpThrowHelper.ThrowImageFormatException("Each transform can only be present once");
                }
            }

            switch (transformType)
            {
            case Vp8LTransformType.SubtractGreen:
                // There is no data associated with this transform.
                break;

            case Vp8LTransformType.ColorIndexingTransform:
                // The transform data contains color table size and the entries in the color table.
                // 8 bit value for color table size.
                uint numColors = this.bitReader.ReadValue(8) + 1;
                int  bits      = numColors > 16 ? 0
                                     : numColors > 4 ? 1
                                     : numColors > 2 ? 2
                                     : 3;
                transform.Bits = bits;
                using (IMemoryOwner <uint> colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false))
                {
                    int finalNumColors = 1 << (8 >> transform.Bits);
                    IMemoryOwner <uint> newColorMap = this.memoryAllocator.Allocate <uint>(finalNumColors, AllocationOptions.Clean);
                    LosslessUtils.ExpandColorMap((int)numColors, colorMap.GetSpan(), newColorMap.GetSpan());
                    transform.Data = newColorMap;
                }

                break;

            case Vp8LTransformType.PredictorTransform:
            case Vp8LTransformType.CrossColorTransform:
            {
                // The first 3 bits of prediction data define the block width and height in number of bits.
                transform.Bits = (int)this.bitReader.ReadValue(3) + 2;
                int blockWidth  = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits);
                int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits);
                IMemoryOwner <uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false);
                transform.Data = transformData;
                break;
            }
            }

            decoder.Transforms.Add(transform);
        }
Пример #2
0
        private int ReadHuffmanCode(int alphabetSize, int[] codeLengths, Span <HuffmanCode> table)
        {
            bool simpleCode = this.bitReader.ReadBit();

            for (int i = 0; i < alphabetSize; i++)
            {
                codeLengths[i] = 0;
            }

            if (simpleCode)
            {
                // (i) Simple Code Length Code.
                // This variant is used in the special case when only 1 or 2 Huffman code lengths are non-zero,
                // and are in the range of[0, 255]. All other Huffman code lengths are implicitly zeros.

                // Read symbols, codes & code lengths directly.
                uint numSymbols         = this.bitReader.ReadValue(1) + 1;
                uint firstSymbolLenCode = this.bitReader.ReadValue(1);

                // The first code is either 1 bit or 8 bit code.
                uint symbol = this.bitReader.ReadValue(firstSymbolLenCode == 0 ? 1 : 8);
                codeLengths[symbol] = 1;

                // The second code (if present), is always 8 bit long.
                if (numSymbols == 2)
                {
                    symbol = this.bitReader.ReadValue(8);
                    codeLengths[symbol] = 1;
                }
            }
            else
            {
                // (ii) Normal Code Length Code:
                // The code lengths of a Huffman code are read as follows: num_code_lengths specifies the number of code lengths;
                // the rest of the code lengths (according to the order in kCodeLengthCodeOrder) are zeros.
                int[] codeLengthCodeLengths = new int[NumCodeLengthCodes];
                uint  numCodes = this.bitReader.ReadValue(4) + 4;
                if (numCodes > NumCodeLengthCodes)
                {
                    WebpThrowHelper.ThrowImageFormatException("Bitstream error, numCodes has an invalid value");
                }

                for (int i = 0; i < numCodes; i++)
                {
                    codeLengthCodeLengths[CodeLengthCodeOrder[i]] = (int)this.bitReader.ReadValue(3);
                }

                this.ReadHuffmanCodeLengths(table, codeLengthCodeLengths, alphabetSize, codeLengths);
            }

            int size = HuffmanUtils.BuildHuffmanTable(table, HuffmanUtils.HuffmanTableBits, codeLengths, alphabetSize);

            return(size);
        }
Пример #3
0
        /// <summary>
        /// The alpha channel of a lossy webp image can be compressed using the lossless webp compression.
        /// This method will undo the compression.
        /// </summary>
        /// <param name="dec">The alpha decoder.</param>
        public void DecodeAlphaData(AlphaDecoder dec)
        {
            Span <uint>       pixelData    = dec.Vp8LDec.Pixels.Memory.Span;
            Span <byte>       data         = MemoryMarshal.Cast <uint, byte>(pixelData);
            int               row          = 0;
            int               col          = 0;
            Vp8LDecoder       vp8LDec      = dec.Vp8LDec;
            int               width        = vp8LDec.Width;
            int               height       = vp8LDec.Height;
            Vp8LMetadata      hdr          = vp8LDec.Metadata;
            int               pos          = 0;              // Current position.
            int               end          = width * height; // End of data.
            int               last         = end;            // Last pixel to decode.
            int               lastRow      = height;
            const int         lenCodeLimit = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes;
            int               mask         = hdr.HuffmanMask;
            Span <HTreeGroup> htreeGroup   = pos < last?GetHTreeGroupForPos(hdr, col, row) : null;

            while (!this.bitReader.Eos && pos < last)
            {
                // Only update when changing tile.
                if ((col & mask) == 0)
                {
                    htreeGroup = GetHTreeGroupForPos(hdr, col, row);
                }

                this.bitReader.FillBitWindow();
                int code = (int)this.ReadSymbol(htreeGroup[0].HTrees[HuffIndex.Green]);
                if (code < WebpConstants.NumLiteralCodes)
                {
                    // Literal
                    data[pos] = (byte)code;
                    ++pos;
                    ++col;

                    if (col >= width)
                    {
                        col = 0;
                        ++row;
                        if (row <= lastRow && row % WebpConstants.NumArgbCacheRows == 0)
                        {
                            dec.ExtractPalettedAlphaRows(row);
                        }
                    }
                }
                else if (code < lenCodeLimit)
                {
                    // Backward reference
                    int lengthSym  = code - WebpConstants.NumLiteralCodes;
                    int length     = this.GetCopyLength(lengthSym);
                    int distSymbol = (int)this.ReadSymbol(htreeGroup[0].HTrees[HuffIndex.Dist]);
                    this.bitReader.FillBitWindow();
                    int distCode = this.GetCopyDistance(distSymbol);
                    int dist     = PlaneCodeToDistance(width, distCode);
                    if (pos >= dist && end - pos >= length)
                    {
                        CopyBlock8B(data, pos, dist, length);
                    }
                    else
                    {
                        WebpThrowHelper.ThrowImageFormatException("error while decoding alpha data");
                    }

                    pos += length;
                    col += length;
                    while (col >= width)
                    {
                        col -= width;
                        ++row;
                        if (row <= lastRow && row % WebpConstants.NumArgbCacheRows == 0)
                        {
                            dec.ExtractPalettedAlphaRows(row);
                        }
                    }

                    if (pos < last && (col & mask) > 0)
                    {
                        htreeGroup = GetHTreeGroupForPos(hdr, col, row);
                    }
                }
                else
                {
                    WebpThrowHelper.ThrowImageFormatException("bitstream error while parsing alpha data");
                }

                this.bitReader.Eos = this.bitReader.IsEndOfStream();
            }

            // Process the remaining rows corresponding to last row-block.
            dec.ExtractPalettedAlphaRows(row > lastRow ? lastRow : row);
        }
Пример #4
0
        private void ReadHuffmanCodeLengths(Span <HuffmanCode> table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths)
        {
            int maxSymbol;
            int symbol      = 0;
            int prevCodeLen = WebpConstants.DefaultCodeLength;
            int size        = HuffmanUtils.BuildHuffmanTable(table, WebpConstants.LengthTableBits, codeLengthCodeLengths, NumCodeLengthCodes);

            if (size == 0)
            {
                WebpThrowHelper.ThrowImageFormatException("Error building huffman table");
            }

            if (this.bitReader.ReadBit())
            {
                int lengthNBits = 2 + (2 * (int)this.bitReader.ReadValue(3));
                maxSymbol = 2 + (int)this.bitReader.ReadValue(lengthNBits);
            }
            else
            {
                maxSymbol = numSymbols;
            }

            while (symbol < numSymbols)
            {
                if (maxSymbol-- == 0)
                {
                    break;
                }

                this.bitReader.FillBitWindow();
                ulong       prefetchBits = this.bitReader.PrefetchBits();
                int         idx          = (int)(prefetchBits & 127);
                HuffmanCode huffmanCode  = table[idx];
                this.bitReader.AdvanceBitPosition(huffmanCode.BitsUsed);
                uint codeLen = huffmanCode.Value;
                if (codeLen < WebpConstants.CodeLengthLiterals)
                {
                    codeLengths[symbol++] = (int)codeLen;
                    if (codeLen != 0)
                    {
                        prevCodeLen = (int)codeLen;
                    }
                }
                else
                {
                    bool usePrev      = codeLen == WebpConstants.CodeLengthRepeatCode;
                    uint slot         = codeLen - WebpConstants.CodeLengthLiterals;
                    int  extraBits    = WebpConstants.CodeLengthExtraBits[slot];
                    int  repeatOffset = WebpConstants.CodeLengthRepeatOffsets[slot];
                    int  repeat       = (int)(this.bitReader.ReadValue(extraBits) + repeatOffset);
                    if (symbol + repeat > numSymbols)
                    {
                        return;
                    }

                    int length = usePrev ? prevCodeLen : 0;
                    while (repeat-- > 0)
                    {
                        codeLengths[symbol++] = length;
                    }
                }
            }
        }
Пример #5
0
        private void ReadHuffmanCodes(Vp8LDecoder decoder, int xSize, int ySize, int colorCacheBits, bool allowRecursion)
        {
            int maxAlphabetSize   = 0;
            int numHTreeGroups    = 1;
            int numHTreeGroupsMax = 1;

            // If the next bit is zero, there is only one meta Huffman code used everywhere in the image. No more data is stored.
            // If this bit is one, the image uses multiple meta Huffman codes. These meta Huffman codes are stored as an entropy image.
            if (allowRecursion && this.bitReader.ReadBit())
            {
                // Use meta Huffman codes.
                int huffmanPrecision = (int)(this.bitReader.ReadValue(3) + 2);
                int huffmanXSize     = LosslessUtils.SubSampleSize(xSize, huffmanPrecision);
                int huffmanYSize     = LosslessUtils.SubSampleSize(ySize, huffmanPrecision);
                int huffmanPixels    = huffmanXSize * huffmanYSize;

                IMemoryOwner <uint> huffmanImage     = this.DecodeImageStream(decoder, huffmanXSize, huffmanYSize, false);
                Span <uint>         huffmanImageSpan = huffmanImage.GetSpan();
                decoder.Metadata.HuffmanSubSampleBits = huffmanPrecision;

                // TODO: Isn't huffmanPixels the length of the span?
                for (int i = 0; i < huffmanPixels; i++)
                {
                    // The huffman data is stored in red and green bytes.
                    uint group = (huffmanImageSpan[i] >> 8) & 0xffff;
                    huffmanImageSpan[i] = group;
                    if (group >= numHTreeGroupsMax)
                    {
                        numHTreeGroupsMax = (int)group + 1;
                    }
                }

                numHTreeGroups = numHTreeGroupsMax;
                decoder.Metadata.HuffmanImage = huffmanImage;
            }

            // Find maximum alphabet size for the hTree group.
            for (int j = 0; j < WebpConstants.HuffmanCodesPerMetaCode; j++)
            {
                int alphabetSize = WebpConstants.AlphabetSize[j];
                if (j == 0 && colorCacheBits > 0)
                {
                    alphabetSize += 1 << colorCacheBits;
                }

                if (maxAlphabetSize < alphabetSize)
                {
                    maxAlphabetSize = alphabetSize;
                }
            }

            int tableSize     = TableSize[colorCacheBits];
            var huffmanTables = new HuffmanCode[numHTreeGroups * tableSize];
            var hTreeGroups   = new HTreeGroup[numHTreeGroups];
            Span <HuffmanCode> huffmanTable = huffmanTables.AsSpan();

            int[] codeLengths = new int[maxAlphabetSize];
            for (int i = 0; i < numHTreeGroupsMax; i++)
            {
                hTreeGroups[i] = new HTreeGroup(HuffmanUtils.HuffmanPackedTableSize);
                HTreeGroup hTreeGroup       = hTreeGroups[i];
                int        totalSize        = 0;
                bool       isTrivialLiteral = true;
                int        maxBits          = 0;
                codeLengths.AsSpan().Clear();
                for (int j = 0; j < WebpConstants.HuffmanCodesPerMetaCode; j++)
                {
                    int alphabetSize = WebpConstants.AlphabetSize[j];
                    if (j == 0 && colorCacheBits > 0)
                    {
                        alphabetSize += 1 << colorCacheBits;
                    }

                    int size = this.ReadHuffmanCode(alphabetSize, codeLengths, huffmanTable);
                    if (size == 0)
                    {
                        WebpThrowHelper.ThrowImageFormatException("Huffman table size is zero");
                    }

                    // TODO: Avoid allocation.
                    hTreeGroup.HTrees.Add(huffmanTable.Slice(0, size).ToArray());

                    HuffmanCode huffTableZero = huffmanTable[0];
                    if (isTrivialLiteral && LiteralMap[j] == 1)
                    {
                        isTrivialLiteral = huffTableZero.BitsUsed == 0;
                    }

                    totalSize   += huffTableZero.BitsUsed;
                    huffmanTable = huffmanTable.Slice(size);

                    if (j <= HuffIndex.Alpha)
                    {
                        int localMaxBits = codeLengths[0];
                        int k;
                        for (k = 1; k < alphabetSize; ++k)
                        {
                            int codeLengthK = codeLengths[k];
                            if (codeLengthK > localMaxBits)
                            {
                                localMaxBits = codeLengthK;
                            }
                        }

                        maxBits += localMaxBits;
                    }
                }

                hTreeGroup.IsTrivialLiteral = isTrivialLiteral;
                hTreeGroup.IsTrivialCode    = false;
                if (isTrivialLiteral)
                {
                    uint red   = hTreeGroup.HTrees[HuffIndex.Red][0].Value;
                    uint blue  = hTreeGroup.HTrees[HuffIndex.Blue][0].Value;
                    uint green = hTreeGroup.HTrees[HuffIndex.Green][0].Value;
                    uint alpha = hTreeGroup.HTrees[HuffIndex.Alpha][0].Value;
                    hTreeGroup.LiteralArb = (alpha << 24) | (red << 16) | blue;
                    if (totalSize == 0 && green < WebpConstants.NumLiteralCodes)
                    {
                        hTreeGroup.IsTrivialCode = true;
                        hTreeGroup.LiteralArb   |= green << 8;
                    }
                }

                hTreeGroup.UsePackedTable = !hTreeGroup.IsTrivialCode && maxBits < HuffmanUtils.HuffmanPackedBits;
                if (hTreeGroup.UsePackedTable)
                {
                    this.BuildPackedTable(hTreeGroup);
                }
            }

            decoder.Metadata.NumHTreeGroups = numHTreeGroups;
            decoder.Metadata.HTreeGroups    = hTreeGroups;
            decoder.Metadata.HuffmanTables  = huffmanTables;
        }
Пример #6
0
        public void DecodeImageData(Vp8LDecoder decoder, Span <uint> pixelData)
        {
            int               lastPixel       = 0;
            int               width           = decoder.Width;
            int               height          = decoder.Height;
            int               row             = lastPixel / width;
            int               col             = lastPixel % width;
            const int         lenCodeLimit    = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes;
            int               colorCacheSize  = decoder.Metadata.ColorCacheSize;
            ColorCache        colorCache      = decoder.Metadata.ColorCache;
            int               colorCacheLimit = lenCodeLimit + colorCacheSize;
            int               mask            = decoder.Metadata.HuffmanMask;
            Span <HTreeGroup> hTreeGroup      = GetHTreeGroupForPos(decoder.Metadata, col, row);

            int totalPixels   = width * height;
            int decodedPixels = 0;
            int lastCached    = decodedPixels;

            while (decodedPixels < totalPixels)
            {
                int code;
                if ((col & mask) == 0)
                {
                    hTreeGroup = GetHTreeGroupForPos(decoder.Metadata, col, row);
                }

                if (hTreeGroup[0].IsTrivialCode)
                {
                    pixelData[decodedPixels] = hTreeGroup[0].LiteralArb;
                    this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
                    continue;
                }

                this.bitReader.FillBitWindow();
                if (hTreeGroup[0].UsePackedTable)
                {
                    code = (int)this.ReadPackedSymbols(hTreeGroup, pixelData, decodedPixels);
                    if (this.bitReader.IsEndOfStream())
                    {
                        break;
                    }

                    if (code == PackedNonLiteralCode)
                    {
                        this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
                        continue;
                    }
                }
                else
                {
                    code = (int)this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Green]);
                }

                if (this.bitReader.IsEndOfStream())
                {
                    break;
                }

                // Literal
                if (code < WebpConstants.NumLiteralCodes)
                {
                    if (hTreeGroup[0].IsTrivialLiteral)
                    {
                        pixelData[decodedPixels] = hTreeGroup[0].LiteralArb | ((uint)code << 8);
                    }
                    else
                    {
                        uint red = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Red]);
                        this.bitReader.FillBitWindow();
                        uint blue  = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Blue]);
                        uint alpha = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Alpha]);
                        if (this.bitReader.IsEndOfStream())
                        {
                            break;
                        }

                        pixelData[decodedPixels] = (uint)(((byte)alpha << 24) | ((byte)red << 16) | ((byte)code << 8) | (byte)blue);
                    }

                    this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
                }
                else if (code < lenCodeLimit)
                {
                    // Backward reference is used.
                    int  lengthSym  = code - WebpConstants.NumLiteralCodes;
                    int  length     = this.GetCopyLength(lengthSym);
                    uint distSymbol = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Dist]);
                    this.bitReader.FillBitWindow();
                    int distCode = this.GetCopyDistance((int)distSymbol);
                    int dist     = PlaneCodeToDistance(width, distCode);
                    if (this.bitReader.IsEndOfStream())
                    {
                        break;
                    }

                    CopyBlock(pixelData, decodedPixels, dist, length);
                    decodedPixels += length;
                    col           += length;
                    while (col >= width)
                    {
                        col -= width;
                        row++;
                    }

                    if ((col & mask) != 0)
                    {
                        hTreeGroup = GetHTreeGroupForPos(decoder.Metadata, col, row);
                    }

                    if (colorCache != null)
                    {
                        while (lastCached < decodedPixels)
                        {
                            colorCache.Insert(pixelData[lastCached]);
                            lastCached++;
                        }
                    }
                }
                else if (code < colorCacheLimit)
                {
                    // Color cache should be used.
                    int key = code - lenCodeLimit;
                    while (lastCached < decodedPixels)
                    {
                        colorCache.Insert(pixelData[lastCached]);
                        lastCached++;
                    }

                    pixelData[decodedPixels] = colorCache.Lookup(key);
                    this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
                }
                else
                {
                    WebpThrowHelper.ThrowImageFormatException("Webp parsing error");
                }
            }
        }
Пример #7
0
        public IMemoryOwner <uint> DecodeImageStream(Vp8LDecoder decoder, int xSize, int ySize, bool isLevel0)
        {
            int transformXSize            = xSize;
            int transformYSize            = ySize;
            int numberOfTransformsPresent = 0;

            if (isLevel0)
            {
                decoder.Transforms = new List <Vp8LTransform>(WebpConstants.MaxNumberOfTransforms);

                // Next bit indicates, if a transformation is present.
                while (this.bitReader.ReadBit())
                {
                    if (numberOfTransformsPresent > WebpConstants.MaxNumberOfTransforms)
                    {
                        WebpThrowHelper.ThrowImageFormatException($"The maximum number of transforms of {WebpConstants.MaxNumberOfTransforms} was exceeded");
                    }

                    this.ReadTransformation(transformXSize, transformYSize, decoder);
                    if (decoder.Transforms[numberOfTransformsPresent].TransformType == Vp8LTransformType.ColorIndexingTransform)
                    {
                        transformXSize = LosslessUtils.SubSampleSize(transformXSize, decoder.Transforms[numberOfTransformsPresent].Bits);
                    }

                    numberOfTransformsPresent++;
                }
            }
            else
            {
                decoder.Metadata = new Vp8LMetadata();
            }

            // Color cache.
            bool isColorCachePresent = this.bitReader.ReadBit();
            int  colorCacheBits      = 0;
            int  colorCacheSize      = 0;

            if (isColorCachePresent)
            {
                colorCacheBits = (int)this.bitReader.ReadValue(4);

                // Note: According to webpinfo color cache bits of 11 are valid, even though 10 is defined in the source code as maximum.
                // That is why 11 bits is also considered valid here.
                bool colorCacheBitsIsValid = colorCacheBits is >= 1 and <= WebpConstants.MaxColorCacheBits + 1;
                if (!colorCacheBitsIsValid)
                {
                    WebpThrowHelper.ThrowImageFormatException("Invalid color cache bits found");
                }
            }

            // Read the Huffman codes (may recurse).
            this.ReadHuffmanCodes(decoder, transformXSize, transformYSize, colorCacheBits, isLevel0);
            decoder.Metadata.ColorCacheSize = colorCacheSize;

            // Finish setting up the color-cache.
            if (isColorCachePresent)
            {
                decoder.Metadata.ColorCache = new ColorCache();
                colorCacheSize = 1 << colorCacheBits;
                decoder.Metadata.ColorCacheSize = colorCacheSize;
                decoder.Metadata.ColorCache.Init(colorCacheBits);
            }
            else
            {
                decoder.Metadata.ColorCacheSize = 0;
            }

            this.UpdateDecoder(decoder, transformXSize, transformYSize);
            if (isLevel0)
            {
                // level 0 complete.
                return(null);
            }

            // Use the Huffman trees to decode the LZ77 encoded data.
            IMemoryOwner <uint> pixelData = this.memoryAllocator.Allocate <uint>(decoder.Width * decoder.Height, AllocationOptions.Clean);

            this.DecodeImageData(decoder, pixelData.GetSpan());

            return(pixelData);
        }