Browse Source

Use memory allocator where possible in lossless decoder

pull/1552/head
Brian Popow 6 years ago
parent
commit
84f55736c7
  1. 37
      src/ImageSharp/Formats/WebP/LosslessUtils.cs
  2. 4
      src/ImageSharp/Formats/WebP/Vp8LMetadata.cs
  3. 3
      src/ImageSharp/Formats/WebP/Vp8LTransform.cs
  4. 2
      src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
  5. 70
      src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs

37
src/ImageSharp/Formats/WebP/LosslessUtils.cs

@ -4,6 +4,8 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
{ {
/// <summary> /// <summary>
@ -15,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// Add green to blue and red channels (i.e. perform the inverse transform of 'subtract green'). /// Add green to blue and red channels (i.e. perform the inverse transform of 'subtract green').
/// </summary> /// </summary>
/// <param name="pixelData">The pixel data to apply the transformation.</param> /// <param name="pixelData">The pixel data to apply the transformation.</param>
public static void AddGreenToBlueAndRed(uint[] pixelData) public static void AddGreenToBlueAndRed(Span<uint> pixelData)
{ {
for (int i = 0; i < pixelData.Length; i++) for (int i = 0; i < pixelData.Length; i++)
{ {
@ -28,12 +30,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void ColorIndexInverseTransform(Vp8LTransform transform, uint[] pixelData) public static void ColorIndexInverseTransform(Vp8LTransform transform, Span<uint> pixelData)
{ {
int bitsPerPixel = 8 >> transform.Bits; int bitsPerPixel = 8 >> transform.Bits;
int width = transform.XSize; int width = transform.XSize;
int height = transform.YSize; int height = transform.YSize;
uint[] colorMap = transform.Data; Span<uint> colorMap = transform.Data.GetSpan();
int decodedPixels = 0; int decodedPixels = 0;
if (bitsPerPixel < 8) if (bitsPerPixel < 8)
{ {
@ -57,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
packedPixels = GetArgbIndex(pixelData[pixelDataPos++]); packedPixels = GetArgbIndex(pixelData[pixelDataPos++]);
} }
decodedPixelData[decodedPixels++] = colorMap[packedPixels & bitMask]; decodedPixelData[decodedPixels++] = colorMap[(int)(packedPixels & bitMask)];
packedPixels >>= bitsPerPixel; packedPixels >>= bitsPerPixel;
} }
} }
@ -72,13 +74,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int x = 0; x < width; ++x) for (int x = 0; x < width; ++x)
{ {
uint colorMapIndex = GetArgbIndex(pixelData[decodedPixels]); uint colorMapIndex = GetArgbIndex(pixelData[decodedPixels]);
pixelData[decodedPixels] = colorMap[colorMapIndex]; pixelData[decodedPixels] = colorMap[(int)colorMapIndex];
decodedPixels++; decodedPixels++;
} }
} }
} }
public static void ColorSpaceInverseTransform(Vp8LTransform transform, uint[] pixelData) public static void ColorSpaceInverseTransform(Vp8LTransform transform, Span<uint> pixelData)
{ {
int width = transform.XSize; int width = transform.XSize;
int yEnd = transform.YSize; int yEnd = transform.YSize;
@ -89,6 +91,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int tilesPerRow = SubSampleSize(width, transform.Bits); int tilesPerRow = SubSampleSize(width, transform.Bits);
int y = 0; int y = 0;
int predRowIdxStart = (y >> transform.Bits) * tilesPerRow; int predRowIdxStart = (y >> transform.Bits) * tilesPerRow;
Span<uint> transformData = transform.Data.GetSpan();
int pixelPos = 0; int pixelPos = 0;
while (y < yEnd) while (y < yEnd)
@ -99,7 +102,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int srcEnd = pixelPos + width; int srcEnd = pixelPos + width;
while (pixelPos < srcSafeEnd) while (pixelPos < srcSafeEnd)
{ {
uint colorCode = transform.Data[predRowIdx++]; uint colorCode = transformData[predRowIdx++];
ColorCodeToMultipliers(colorCode, ref m); ColorCodeToMultipliers(colorCode, ref m);
TransformColorInverse(m, pixelData, pixelPos, tileWidth); TransformColorInverse(m, pixelData, pixelPos, tileWidth);
pixelPos += tileWidth; pixelPos += tileWidth;
@ -107,7 +110,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (pixelPos < srcEnd) if (pixelPos < srcEnd)
{ {
uint colorCode = transform.Data[predRowIdx]; uint colorCode = transformData[predRowIdx];
ColorCodeToMultipliers(colorCode, ref m); ColorCodeToMultipliers(colorCode, ref m);
TransformColorInverse(m, pixelData, pixelPos, remainingWidth); TransformColorInverse(m, pixelData, pixelPos, remainingWidth);
pixelPos += remainingWidth; pixelPos += remainingWidth;
@ -121,7 +124,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void TransformColorInverse(Vp8LMultipliers m, uint[] pixelData, int start, int numPixels) public static void TransformColorInverse(Vp8LMultipliers m, Span<uint> pixelData, int start, int numPixels)
{ {
int end = start + numPixels; int end = start + numPixels;
for (int i = start; i < end; i++) for (int i = start; i < end; i++)
@ -141,14 +144,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static uint[] ExpandColorMap(int numColors, Vp8LTransform transform, uint[] transformData) public static void ExpandColorMap(int numColors, Span<uint> transformData, Span<uint> newColorMap)
{ {
int finalNumColors = 1 << (8 >> transform.Bits);
// TODO: use memoryAllocator here
var newColorMap = new uint[finalNumColors];
newColorMap[0] = transformData[0]; newColorMap[0] = transformData[0];
Span<byte> data = MemoryMarshal.Cast<uint, byte>(transformData); Span<byte> data = MemoryMarshal.Cast<uint, byte>(transformData);
Span<byte> newData = MemoryMarshal.Cast<uint, byte>(newColorMap); Span<byte> newData = MemoryMarshal.Cast<uint, byte>(newColorMap);
int i; int i;
@ -158,19 +156,18 @@ namespace SixLabors.ImageSharp.Formats.WebP
newData[i] = (byte)((data[i] + newData[i - 4]) & 0xff); newData[i] = (byte)((data[i] + newData[i - 4]) & 0xff);
} }
for (; i < 4 * finalNumColors; ++i) for (; i < 4 * newColorMap.Length; ++i)
{ {
newData[i] = 0; // black tail. newData[i] = 0; // black tail.
} }
return newColorMap;
} }
public static void PredictorInverseTransform(Vp8LTransform transform, uint[] pixelData, Span<uint> output) public static void PredictorInverseTransform(Vp8LTransform transform, Span<uint> pixelData, Span<uint> output)
{ {
int processedPixels = 0; int processedPixels = 0;
int yStart = 0; int yStart = 0;
int width = transform.XSize; int width = transform.XSize;
Span<uint> transformData = transform.Data.GetSpan();
// First Row follows the L (mode=1) mode. // First Row follows the L (mode=1) mode.
PredictorAdd0(pixelData, processedPixels, 1, output); PredictorAdd0(pixelData, processedPixels, 1, output);
@ -194,7 +191,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
while (x < width) while (x < width)
{ {
uint predictorMode = (transform.Data[predictorModeIdx++] >> 8) & 0xf; uint predictorMode = (transformData[predictorModeIdx++] >> 8) & 0xf;
int xEnd = (x & ~mask) + tileWidth; int xEnd = (x & ~mask) + tileWidth;
if (xEnd > width) if (xEnd > width)
{ {

4
src/ImageSharp/Formats/WebP/Vp8LMetadata.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Buffers;
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
{ {
internal class Vp8LMetadata internal class Vp8LMetadata
@ -15,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public int HuffmanXSize { get; set; } public int HuffmanXSize { get; set; }
public uint[] HuffmanImage { get; set; } public IMemoryOwner<uint> HuffmanImage { get; set; }
public int NumHTreeGroups { get; set; } public int NumHTreeGroups { get; set; }

3
src/ImageSharp/Formats/WebP/Vp8LTransform.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Buffers;
using System.Diagnostics; using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
@ -41,6 +42,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary> /// <summary>
/// Gets or sets the transform data. /// Gets or sets the transform data.
/// </summary> /// </summary>
public uint[] Data { get; set; } public IMemoryOwner<uint> Data { get; set; }
} }
} }

2
src/ImageSharp/Formats/WebP/WebPDecoderCore.cs

@ -306,7 +306,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
/// <summary> /// <summary>
/// Reads the header of lossless webp image. /// Reads the header of a lossless webp image.
/// </summary> /// </summary>
/// <param name="features">Webp image features.</param> /// <param name="features">Webp image features.</param>
/// <returns>Information about this image.</returns> /// <returns>Information about this image.</returns>

70
src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs

@ -99,11 +99,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
var decoder = new Vp8LDecoder(width, height); var decoder = new Vp8LDecoder(width, height);
uint[] pixelData = this.DecodeImageStream(decoder, width, height, true); IMemoryOwner<uint> pixelData = this.DecodeImageStream(decoder, width, height, true);
this.DecodePixelValues(decoder, pixelData, pixels); this.DecodePixelValues(decoder, pixelData.GetSpan(), pixels);
// Free up allocated memory.
pixelData.Dispose();
foreach (Vp8LTransform transform in decoder.Transforms)
{
transform.Data?.Dispose();
}
decoder.Metadata?.HuffmanImage?.Dispose();
} }
private uint[] DecodeImageStream(Vp8LDecoder decoder, int xSize, int ySize, bool isLevel0) private IMemoryOwner<uint> DecodeImageStream(Vp8LDecoder decoder, int xSize, int ySize, bool isLevel0)
{ {
int numberOfTransformsPresent = 0; int numberOfTransformsPresent = 0;
if (isLevel0) if (isLevel0)
@ -162,7 +170,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.UpdateDecoder(decoder, xSize, ySize); this.UpdateDecoder(decoder, xSize, ySize);
uint[] pixelData = this.DecodeImageData(decoder, colorCacheSize, colorCache); IMemoryOwner<uint> pixelData = this.memoryAllocator.Allocate<uint>(decoder.Width * decoder.Height, AllocationOptions.Clean);
this.DecodeImageData(decoder, pixelData.GetSpan(), colorCacheSize, colorCache);
if (!isLevel0) if (!isLevel0)
{ {
decoder.Metadata = new Vp8LMetadata(); decoder.Metadata = new Vp8LMetadata();
@ -171,7 +180,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
return pixelData; return pixelData;
} }
private void DecodePixelValues<TPixel>(Vp8LDecoder decoder, uint[] pixelData, Buffer2D<TPixel> pixels) private void DecodePixelValues<TPixel>(Vp8LDecoder decoder, Span<uint> pixelData, Buffer2D<TPixel> pixels)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
// Apply reverse transformations, if any are present. // Apply reverse transformations, if any are present.
@ -195,7 +204,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
private uint[] DecodeImageData(Vp8LDecoder decoder, int colorCacheSize, ColorCache colorCache) private void DecodeImageData(Vp8LDecoder decoder, Span<uint> pixelData, int colorCacheSize, ColorCache colorCache)
{ {
int lastPixel = 0; int lastPixel = 0;
int width = decoder.Width; int width = decoder.Width;
@ -206,8 +215,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
int colorCacheLimit = lenCodeLimit + colorCacheSize; int colorCacheLimit = lenCodeLimit + colorCacheSize;
int mask = decoder.Metadata.HuffmanMask; int mask = decoder.Metadata.HuffmanMask;
HTreeGroup[] hTreeGroup = this.GetHTreeGroupForPos(decoder.Metadata, col, row); HTreeGroup[] hTreeGroup = this.GetHTreeGroupForPos(decoder.Metadata, col, row);
// TODO: use memory allocator
var pixelData = new uint[width * height];
int totalPixels = width * height; int totalPixels = width * height;
int decodedPixels = 0; int decodedPixels = 0;
@ -331,11 +338,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
WebPThrowHelper.ThrowImageFormatException("Webp parsing error"); WebPThrowHelper.ThrowImageFormatException("Webp parsing error");
} }
} }
return pixelData;
} }
private void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels, uint[] pixelData, ref int lastCached) private void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels, Span<uint> pixelData, ref int lastCached)
{ {
++col; ++col;
decodedPixels++; decodedPixels++;
@ -369,14 +374,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint huffmanPrecision = this.bitReader.ReadBits(3) + 2; uint huffmanPrecision = this.bitReader.ReadBits(3) + 2;
int huffmanXSize = LosslessUtils.SubSampleSize(xSize, (int)huffmanPrecision); int huffmanXSize = LosslessUtils.SubSampleSize(xSize, (int)huffmanPrecision);
int huffmanYSize = LosslessUtils.SubSampleSize(ySize, (int)huffmanPrecision); int huffmanYSize = LosslessUtils.SubSampleSize(ySize, (int)huffmanPrecision);
int huffmanPixs = huffmanXSize * huffmanYSize; int huffmanPixels = huffmanXSize * huffmanYSize;
uint[] huffmanImage = this.DecodeImageStream(decoder, huffmanXSize, huffmanYSize, false); IMemoryOwner<uint> huffmanImage = this.DecodeImageStream(decoder, huffmanXSize, huffmanYSize, false);
Span<uint> huffmanImageSpan = huffmanImage.GetSpan();
decoder.Metadata.HuffmanSubSampleBits = (int)huffmanPrecision; decoder.Metadata.HuffmanSubSampleBits = (int)huffmanPrecision;
for (int i = 0; i < huffmanPixs; ++i) for (int i = 0; i < huffmanPixels; ++i)
{ {
// The huffman data is stored in red and green bytes. // The huffman data is stored in red and green bytes.
uint group = (huffmanImage[i] >> 8) & 0xffff; uint group = (huffmanImageSpan[i] >> 8) & 0xffff;
huffmanImage[i] = group; huffmanImageSpan[i] = group;
if (group >= numHTreeGroupsMax) if (group >= numHTreeGroupsMax)
{ {
numHTreeGroupsMax = (int)group + 1; numHTreeGroupsMax = (int)group + 1;
@ -625,19 +631,26 @@ namespace SixLabors.ImageSharp.Formats.WebP
: 3; : 3;
transform.XSize = LosslessUtils.SubSampleSize(transform.XSize, bits); transform.XSize = LosslessUtils.SubSampleSize(transform.XSize, bits);
transform.Bits = bits; transform.Bits = bits;
uint[] colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false); using (IMemoryOwner<uint> colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false))
transform.Data = LosslessUtils.ExpandColorMap((int)numColors, transform, colorMap); {
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; break;
case Vp8LTransformType.PredictorTransform: case Vp8LTransformType.PredictorTransform:
case Vp8LTransformType.CrossColorTransform: case Vp8LTransformType.CrossColorTransform:
{ {
transform.Bits = (int)this.bitReader.ReadBits(3) + 2; transform.Bits = (int)this.bitReader.ReadBits(3) + 2;
transform.Data = this.DecodeImageStream( IMemoryOwner<uint> transformData = this.DecodeImageStream(
decoder, decoder,
LosslessUtils.SubSampleSize(transform.XSize, transform.Bits), LosslessUtils.SubSampleSize(transform.XSize, transform.Bits),
LosslessUtils.SubSampleSize(transform.YSize, transform.Bits), LosslessUtils.SubSampleSize(transform.YSize, transform.Bits),
false); false);
transform.Data = transformData;
break; break;
} }
} }
@ -650,7 +663,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary> /// </summary>
/// <param name="decoder">The decoder holding the transformation infos.</param> /// <param name="decoder">The decoder holding the transformation infos.</param>
/// <param name="pixelData">The pixel data to apply the transformation.</param> /// <param name="pixelData">The pixel data to apply the transformation.</param>
private void ApplyInverseTransforms(Vp8LDecoder decoder, uint[] pixelData) private void ApplyInverseTransforms(Vp8LDecoder decoder, Span<uint> pixelData)
{ {
List<Vp8LTransform> transforms = decoder.Transforms; List<Vp8LTransform> transforms = decoder.Transforms;
for (int i = transforms.Count - 1; i >= 0; i--) for (int i = transforms.Count - 1; i >= 0; i--)
@ -710,7 +723,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
return tableSpan[0].Value; return tableSpan[0].Value;
} }
private uint ReadPackedSymbols(HTreeGroup[] group, uint[] pixelData, int decodedPixels) private uint ReadPackedSymbols(HTreeGroup[] group, Span<uint> pixelData, int decodedPixels)
{ {
uint val = (uint)(this.bitReader.PrefetchBits() & (HuffmanUtils.HuffmanPackedTableSize - 1)); uint val = (uint)(this.bitReader.PrefetchBits() & (HuffmanUtils.HuffmanPackedTableSize - 1));
HuffmanCode code = group[0].PackedTable[val]; HuffmanCode code = group[0].PackedTable[val];
@ -726,18 +739,18 @@ namespace SixLabors.ImageSharp.Formats.WebP
return code.Value; return code.Value;
} }
private void CopyBlock(uint[] pixelData, int decodedPixels, int dist, int length) private void CopyBlock(Span<uint> pixelData, int decodedPixels, int dist, int length)
{ {
if (dist >= length) if (dist >= length)
{ {
Span<uint> src = pixelData.AsSpan(decodedPixels - dist, length); Span<uint> src = pixelData.Slice(decodedPixels - dist, length);
Span<uint> dest = pixelData.AsSpan(decodedPixels); Span<uint> dest = pixelData.Slice(decodedPixels);
src.CopyTo(dest); src.CopyTo(dest);
} }
else else
{ {
Span<uint> src = pixelData.AsSpan(decodedPixels - dist); Span<uint> src = pixelData.Slice(decodedPixels - dist);
Span<uint> dest = pixelData.AsSpan(decodedPixels); Span<uint> dest = pixelData.Slice(decodedPixels);
for (int i = 0; i < length; ++i) for (int i = 0; i < length; ++i)
{ {
dest[i] = src[i]; dest[i] = src[i];
@ -811,14 +824,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
return hCode.BitsUsed; return hCode.BitsUsed;
} }
private uint GetMetaIndex(uint[] image, int xSize, int bits, int x, int y) private uint GetMetaIndex(IMemoryOwner<uint> huffmanImage, int xSize, int bits, int x, int y)
{ {
if (bits is 0) if (bits is 0)
{ {
return 0; return 0;
} }
return image[(xSize * (y >> bits)) + (x >> bits)]; Span<uint> huffmanImageSpan = huffmanImage.GetSpan();
return huffmanImageSpan[(xSize * (y >> bits)) + (x >> bits)];
} }
private HTreeGroup[] GetHTreeGroupForPos(Vp8LMetadata metadata, int x, int y) private HTreeGroup[] GetHTreeGroupForPos(Vp8LMetadata metadata, int x, int y)

Loading…
Cancel
Save