From 864aaa52a2efa8ed5657a8d21a13cfde0cdb7487 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 10 Jan 2020 18:51:56 +0100 Subject: [PATCH] Use memory allocator --- src/ImageSharp/Formats/WebP/LosslessUtils.cs | 62 +++++++++---------- src/ImageSharp/Formats/WebP/Vp8LMetadata.cs | 2 - .../Formats/WebP/WebPDecoderCore.cs | 14 ++++- src/ImageSharp/Formats/WebP/WebPImageInfo.cs | 6 +- .../Formats/WebP/WebPLosslessDecoder.cs | 38 ++++++++---- 5 files changed, 70 insertions(+), 52 deletions(-) diff --git a/src/ImageSharp/Formats/WebP/LosslessUtils.cs b/src/ImageSharp/Formats/WebP/LosslessUtils.cs index f2a754a92..4ca97b371 100644 --- a/src/ImageSharp/Formats/WebP/LosslessUtils.cs +++ b/src/ImageSharp/Formats/WebP/LosslessUtils.cs @@ -41,7 +41,6 @@ namespace SixLabors.ImageSharp.Formats.WebP int countMask = pixelsPerByte - 1; int bitMask = (1 << bitsPerPixel) - 1; - // TODO: use memoryAllocator here var decodedPixelData = new uint[width * height]; int pixelDataPos = 0; for (int y = 0; y < height; ++y) @@ -167,11 +166,8 @@ namespace SixLabors.ImageSharp.Formats.WebP return newColorMap; } - public static void PredictorInverseTransform(Vp8LTransform transform, uint[] pixelData) + public static void PredictorInverseTransform(Vp8LTransform transform, uint[] pixelData, Span output) { - // TODO: use memory allocator instead - var output = new uint[pixelData.Length]; - int processedPixels = 0; int yStart = 0; int width = transform.XSize; @@ -265,10 +261,10 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - output.AsSpan().CopyTo(pixelData); + output.CopyTo(pixelData); } - private static void PredictorAdd0(uint[] input, int startIdx, int numberOfPixels, uint[] output) + private static void PredictorAdd0(Span input, int startIdx, int numberOfPixels, Span output) { int endIdx = startIdx + numberOfPixels; for (int x = startIdx; x < endIdx; ++x) @@ -278,7 +274,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd1(uint[] input, int startIdx, int numberOfPixels, uint[] output) + private static void PredictorAdd1(Span input, int startIdx, int numberOfPixels, Span output) { int endIdx = startIdx + numberOfPixels; uint left = output[startIdx - 1]; @@ -288,7 +284,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd2(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd2(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -299,7 +295,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd3(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd3(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -310,7 +306,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd4(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd4(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -321,7 +317,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd5(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd5(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -332,7 +328,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd6(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd6(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -343,7 +339,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd7(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd7(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -354,7 +350,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd8(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd8(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -365,7 +361,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd9(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd9(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -376,7 +372,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd10(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd10(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -387,7 +383,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd11(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd11(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -398,7 +394,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd12(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd12(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -409,7 +405,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private static void PredictorAdd13(uint[] input, int startIdx, int numberOfPixels, int width, uint[] output) + private static void PredictorAdd13(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; int offset = 0; @@ -425,75 +421,75 @@ namespace SixLabors.ImageSharp.Formats.WebP return WebPConstants.ArgbBlack; } - private static uint Predictor1(uint left, uint[] top) + private static uint Predictor1(uint left, Span top) { return left; } - private static uint Predictor2(uint left, uint[] top, int idx) + private static uint Predictor2(uint left, Span top, int idx) { return top[idx]; } - private static uint Predictor3(uint left, uint[] top, int idx) + private static uint Predictor3(uint left, Span top, int idx) { return top[idx + 1]; } - private static uint Predictor4(uint left, uint[] top, int idx) + private static uint Predictor4(uint left, Span top, int idx) { return top[idx - 1]; } - private static uint Predictor5(uint left, uint[] top, int idx) + private static uint Predictor5(uint left, Span top, int idx) { uint pred = Average3(left, top[idx], top[idx + 1]); return pred; } - private static uint Predictor6(uint left, uint[] top, int idx) + private static uint Predictor6(uint left, Span top, int idx) { uint pred = Average2(left, top[idx - 1]); return pred; } - private static uint Predictor7(uint left, uint[] top, int idx) + private static uint Predictor7(uint left, Span top, int idx) { uint pred = Average2(left, top[idx]); return pred; } - private static uint Predictor8(uint left, uint[] top, int idx) + private static uint Predictor8(uint left, Span top, int idx) { uint pred = Average2(top[idx - 1], top[idx]); return pred; } - private static uint Predictor9(uint left, uint[] top, int idx) + private static uint Predictor9(uint left, Span top, int idx) { uint pred = Average2(top[idx], top[idx + 1]); return pred; } - private static uint Predictor10(uint left, uint[] top, int idx) + private static uint Predictor10(uint left, Span top, int idx) { uint pred = Average4(left, top[idx - 1], top[idx], top[idx + 1]); return pred; } - private static uint Predictor11(uint left, uint[] top, int idx) + private static uint Predictor11(uint left, Span top, int idx) { uint pred = Select(top[idx], left, top[idx - 1]); return pred; } - private static uint Predictor12(uint left, uint[] top, int idx) + private static uint Predictor12(uint left, Span top, int idx) { uint pred = ClampedAddSubtractFull(left, top[idx], top[idx - 1]); return pred; } - private static uint Predictor13(uint left, uint[] top, int idx) + private static uint Predictor13(uint left, Span top, int idx) { uint pred = ClampedAddSubtractHalf(left, top[idx], top[idx - 1]); return pred; diff --git a/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs b/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs index edc72a822..25ecea6b8 100644 --- a/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs +++ b/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs @@ -9,8 +9,6 @@ namespace SixLabors.ImageSharp.Formats.WebP public ColorCache ColorCache { get; set; } - public ColorCache SavedColorCache { get; set; } - public int HuffmanMask { get; set; } public int HuffmanSubSampleBits { get; set; } diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs index 0e5e79994..667212f90 100644 --- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Formats.WebP Buffer2D pixels = image.GetRootFramePixelBuffer(); if (imageInfo.IsLossLess) { - var losslessDecoder = new WebPLosslessDecoder(imageInfo.Vp9LBitReader); + var losslessDecoder = new WebPLosslessDecoder(imageInfo.Vp9LBitReader, this.memoryAllocator); losslessDecoder.Decode(pixels, image.Width, image.Height); } else @@ -256,6 +256,11 @@ namespace SixLabors.ImageSharp.Formats.WebP return new WebPImageInfo(); } + /// + /// Reads the header of a lossy webp image. + /// + /// Webp features. + /// Information about this webp image. private WebPImageInfo ReadVp8Header(WebPFeatures features = null) { this.webpMetadata.Format = WebPFormatType.Lossy; @@ -300,6 +305,11 @@ namespace SixLabors.ImageSharp.Formats.WebP }; } + /// + /// Reads the header of lossless webp image. + /// + /// Webp image features. + /// Information about this image. private WebPImageInfo ReadVp8LHeader(WebPFeatures features = null) { this.webpMetadata.Format = WebPFormatType.Lossless; @@ -319,7 +329,7 @@ namespace SixLabors.ImageSharp.Formats.WebP uint width = bitReader.ReadBits(WebPConstants.Vp8LImageSizeBits) + 1; uint height = bitReader.ReadBits(WebPConstants.Vp8LImageSizeBits) + 1; - // The alpha_is_used flag should be set to 0 when all alpha values are 255 in the picture, and 1 otherwise. + // The alphaIsUsed flag should be set to 0 when all alpha values are 255 in the picture, and 1 otherwise. bool alphaIsUsed = bitReader.ReadBit(); // The next 3 bits are the version. The version_number is a 3 bit code that must be set to 0. diff --git a/src/ImageSharp/Formats/WebP/WebPImageInfo.cs b/src/ImageSharp/Formats/WebP/WebPImageInfo.cs index 35e27f721..cf692289a 100644 --- a/src/ImageSharp/Formats/WebP/WebPImageInfo.cs +++ b/src/ImageSharp/Formats/WebP/WebPImageInfo.cs @@ -30,7 +30,9 @@ namespace SixLabors.ImageSharp.Formats.WebP /// public uint ImageDataSize { get; set; } - // TODO: not sure if the bitreader is in the right place here, but for the sake of simplicity it will stay here for now. Will be refactored later. - public Vp8LBitReader Vp9LBitReader { get; set; } + /// + /// Gets or sets Vp8L bitreader. Will be null if its not lossless image. + /// + public Vp8LBitReader Vp9LBitReader { get; set; } = null; } } diff --git a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs index 887ed8691..d878a0d2b 100644 --- a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs @@ -2,11 +2,13 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Collections.Generic; using System.Linq; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.WebP { @@ -70,13 +72,20 @@ namespace SixLabors.ImageSharp.Formats.WebP 0, 1, 1, 1, 0 }; + /// + /// Used for allocating memory during processing operations. + /// + private readonly MemoryAllocator memoryAllocator; + /// /// Initializes a new instance of the class. /// /// Bitreader to read from the stream. - public WebPLosslessDecoder(Vp8LBitReader bitReader) + /// Used for allocating memory during processing operations. + public WebPLosslessDecoder(Vp8LBitReader bitReader, MemoryAllocator memoryAllocator) { this.bitReader = bitReader; + this.memoryAllocator = memoryAllocator; } /// @@ -197,6 +206,7 @@ namespace SixLabors.ImageSharp.Formats.WebP int colorCacheLimit = lenCodeLimit + colorCacheSize; int mask = decoder.Metadata.HuffmanMask; HTreeGroup[] hTreeGroup = this.GetHTreeGroupForPos(decoder.Metadata, col, row); + // TODO: use memory allocator var pixelData = new uint[width * height]; int totalPixels = width * height; @@ -412,7 +422,7 @@ namespace SixLabors.ImageSharp.Formats.WebP alphabetSize += 1 << colorCacheBits; } - int size = this.ReadHuffmanCode(decoder, alphabetSize, codeLengths, huffmanTable); + int size = this.ReadHuffmanCode(alphabetSize, codeLengths, huffmanTable); if (size is 0) { WebPThrowHelper.ThrowImageFormatException("Huffman table size is zero"); @@ -472,7 +482,7 @@ namespace SixLabors.ImageSharp.Formats.WebP decoder.Metadata.HuffmanTables = huffmanTables; } - private int ReadHuffmanCode(Vp8LDecoder decoder, int alphabetSize, int[] codeLengths, Span table) + private int ReadHuffmanCode(int alphabetSize, int[] codeLengths, Span table) { bool simpleCode = this.bitReader.ReadBit(); for (int i = 0; i < alphabetSize; i++) @@ -491,7 +501,7 @@ namespace SixLabors.ImageSharp.Formats.WebP uint firstSymbolLenCode = this.bitReader.ReadBits(1); // The first code is either 1 bit or 8 bit code. - uint symbol = this.bitReader.ReadBits((firstSymbolLenCode == 0) ? 1 : 8); + uint symbol = this.bitReader.ReadBits((firstSymbolLenCode is 0) ? 1 : 8); codeLengths[symbol] = 1; // The second code (if present), is always 8 bit long. @@ -518,7 +528,7 @@ namespace SixLabors.ImageSharp.Formats.WebP codeLengthCodeLengths[KCodeLengthCodeOrder[i]] = (int)this.bitReader.ReadBits(3); } - this.ReadHuffmanCodeLengths(decoder, table.ToArray(), codeLengthCodeLengths, alphabetSize, codeLengths); + this.ReadHuffmanCodeLengths(table.ToArray(), codeLengthCodeLengths, alphabetSize, codeLengths); } int size = HuffmanUtils.BuildHuffmanTable(table, HuffmanUtils.HuffmanTableBits, codeLengths, alphabetSize); @@ -526,7 +536,7 @@ namespace SixLabors.ImageSharp.Formats.WebP return size; } - private void ReadHuffmanCodeLengths(Vp8LDecoder decoder, HuffmanCode[] table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths) + private void ReadHuffmanCodeLengths(HuffmanCode[] table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths) { int maxSymbol; int symbol = 0; @@ -580,13 +590,11 @@ namespace SixLabors.ImageSharp.Formats.WebP // TODO: not sure, if this should be treated as an error here return; } - else + + int length = usePrev ? prevCodeLen : 0; + while (repeat-- > 0) { - int length = usePrev ? prevCodeLen : 0; - while (repeat-- > 0) - { - codeLengths[symbol++] = length; - } + codeLengths[symbol++] = length; } } } @@ -651,7 +659,11 @@ namespace SixLabors.ImageSharp.Formats.WebP switch (transformType) { case Vp8LTransformType.PredictorTransform: - LosslessUtils.PredictorInverseTransform(transforms[i], pixelData); + using (IMemoryOwner output = this.memoryAllocator.Allocate(pixelData.Length, AllocationOptions.Clean)) + { + LosslessUtils.PredictorInverseTransform(transforms[i], pixelData, output.GetSpan()); + } + break; case Vp8LTransformType.SubtractGreen: LosslessUtils.AddGreenToBlueAndRed(pixelData);