diff --git a/src/ImageSharp/Formats/WebP/LosslessUtils.cs b/src/ImageSharp/Formats/WebP/LosslessUtils.cs
index 4ca97b3718..7480318606 100644
--- a/src/ImageSharp/Formats/WebP/LosslessUtils.cs
+++ b/src/ImageSharp/Formats/WebP/LosslessUtils.cs
@@ -4,6 +4,8 @@
using System;
using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.Memory;
+
namespace SixLabors.ImageSharp.Formats.WebP
{
///
@@ -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').
///
/// The pixel data to apply the transformation.
- public static void AddGreenToBlueAndRed(uint[] pixelData)
+ public static void AddGreenToBlueAndRed(Span pixelData)
{
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 pixelData)
{
int bitsPerPixel = 8 >> transform.Bits;
int width = transform.XSize;
int height = transform.YSize;
- uint[] colorMap = transform.Data;
+ Span colorMap = transform.Data.GetSpan();
int decodedPixels = 0;
if (bitsPerPixel < 8)
{
@@ -57,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
packedPixels = GetArgbIndex(pixelData[pixelDataPos++]);
}
- decodedPixelData[decodedPixels++] = colorMap[packedPixels & bitMask];
+ decodedPixelData[decodedPixels++] = colorMap[(int)(packedPixels & bitMask)];
packedPixels >>= bitsPerPixel;
}
}
@@ -72,13 +74,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int x = 0; x < width; ++x)
{
uint colorMapIndex = GetArgbIndex(pixelData[decodedPixels]);
- pixelData[decodedPixels] = colorMap[colorMapIndex];
+ pixelData[decodedPixels] = colorMap[(int)colorMapIndex];
decodedPixels++;
}
}
}
- public static void ColorSpaceInverseTransform(Vp8LTransform transform, uint[] pixelData)
+ public static void ColorSpaceInverseTransform(Vp8LTransform transform, Span pixelData)
{
int width = transform.XSize;
int yEnd = transform.YSize;
@@ -89,6 +91,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int tilesPerRow = SubSampleSize(width, transform.Bits);
int y = 0;
int predRowIdxStart = (y >> transform.Bits) * tilesPerRow;
+ Span transformData = transform.Data.GetSpan();
int pixelPos = 0;
while (y < yEnd)
@@ -99,7 +102,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int srcEnd = pixelPos + width;
while (pixelPos < srcSafeEnd)
{
- uint colorCode = transform.Data[predRowIdx++];
+ uint colorCode = transformData[predRowIdx++];
ColorCodeToMultipliers(colorCode, ref m);
TransformColorInverse(m, pixelData, pixelPos, tileWidth);
pixelPos += tileWidth;
@@ -107,7 +110,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (pixelPos < srcEnd)
{
- uint colorCode = transform.Data[predRowIdx];
+ uint colorCode = transformData[predRowIdx];
ColorCodeToMultipliers(colorCode, ref m);
TransformColorInverse(m, pixelData, 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 pixelData, int start, int numPixels)
{
int end = start + numPixels;
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 transformData, Span newColorMap)
{
- int finalNumColors = 1 << (8 >> transform.Bits);
-
- // TODO: use memoryAllocator here
- var newColorMap = new uint[finalNumColors];
newColorMap[0] = transformData[0];
-
Span data = MemoryMarshal.Cast(transformData);
Span newData = MemoryMarshal.Cast(newColorMap);
int i;
@@ -158,19 +156,18 @@ namespace SixLabors.ImageSharp.Formats.WebP
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.
}
-
- return newColorMap;
}
- public static void PredictorInverseTransform(Vp8LTransform transform, uint[] pixelData, Span output)
+ public static void PredictorInverseTransform(Vp8LTransform transform, Span pixelData, Span output)
{
int processedPixels = 0;
int yStart = 0;
int width = transform.XSize;
+ Span transformData = transform.Data.GetSpan();
// First Row follows the L (mode=1) mode.
PredictorAdd0(pixelData, processedPixels, 1, output);
@@ -194,7 +191,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
while (x < width)
{
- uint predictorMode = (transform.Data[predictorModeIdx++] >> 8) & 0xf;
+ uint predictorMode = (transformData[predictorModeIdx++] >> 8) & 0xf;
int xEnd = (x & ~mask) + tileWidth;
if (xEnd > width)
{
diff --git a/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs b/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs
index 25ecea6b8e..737def7f41 100644
--- a/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs
+++ b/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System.Buffers;
+
namespace SixLabors.ImageSharp.Formats.WebP
{
internal class Vp8LMetadata
@@ -15,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public int HuffmanXSize { get; set; }
- public uint[] HuffmanImage { get; set; }
+ public IMemoryOwner HuffmanImage { get; set; }
public int NumHTreeGroups { get; set; }
diff --git a/src/ImageSharp/Formats/WebP/Vp8LTransform.cs b/src/ImageSharp/Formats/WebP/Vp8LTransform.cs
index 78874554a1..ae62123d31 100644
--- a/src/ImageSharp/Formats/WebP/Vp8LTransform.cs
+++ b/src/ImageSharp/Formats/WebP/Vp8LTransform.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System.Buffers;
using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.WebP
@@ -41,6 +42,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
/// Gets or sets the transform data.
///
- public uint[] Data { get; set; }
+ public IMemoryOwner Data { get; set; }
}
}
diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
index 667212f907..b12ed0e726 100644
--- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
+++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
@@ -306,7 +306,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
///
- /// Reads the header of lossless webp image.
+ /// Reads the header of a lossless webp image.
///
/// Webp image features.
/// Information about this image.
diff --git a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
index d878a0d2bd..ecdaf606c3 100644
--- a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
@@ -99,11 +99,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
where TPixel : struct, IPixel
{
var decoder = new Vp8LDecoder(width, height);
- uint[] pixelData = this.DecodeImageStream(decoder, width, height, true);
- this.DecodePixelValues(decoder, pixelData, pixels);
+ IMemoryOwner pixelData = this.DecodeImageStream(decoder, width, height, true);
+ 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 DecodeImageStream(Vp8LDecoder decoder, int xSize, int ySize, bool isLevel0)
{
int numberOfTransformsPresent = 0;
if (isLevel0)
@@ -162,7 +170,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.UpdateDecoder(decoder, xSize, ySize);
- uint[] pixelData = this.DecodeImageData(decoder, colorCacheSize, colorCache);
+ IMemoryOwner pixelData = this.memoryAllocator.Allocate(decoder.Width * decoder.Height, AllocationOptions.Clean);
+ this.DecodeImageData(decoder, pixelData.GetSpan(), colorCacheSize, colorCache);
if (!isLevel0)
{
decoder.Metadata = new Vp8LMetadata();
@@ -171,7 +180,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
return pixelData;
}
- private void DecodePixelValues(Vp8LDecoder decoder, uint[] pixelData, Buffer2D pixels)
+ private void DecodePixelValues(Vp8LDecoder decoder, Span pixelData, Buffer2D pixels)
where TPixel : struct, IPixel
{
// 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 pixelData, int colorCacheSize, ColorCache colorCache)
{
int lastPixel = 0;
int width = decoder.Width;
@@ -206,8 +215,6 @@ 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;
int decodedPixels = 0;
@@ -331,11 +338,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
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 pixelData, ref int lastCached)
{
++col;
decodedPixels++;
@@ -369,14 +374,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint huffmanPrecision = this.bitReader.ReadBits(3) + 2;
int huffmanXSize = LosslessUtils.SubSampleSize(xSize, (int)huffmanPrecision);
int huffmanYSize = LosslessUtils.SubSampleSize(ySize, (int)huffmanPrecision);
- int huffmanPixs = huffmanXSize * huffmanYSize;
- uint[] huffmanImage = this.DecodeImageStream(decoder, huffmanXSize, huffmanYSize, false);
+ int huffmanPixels = huffmanXSize * huffmanYSize;
+ IMemoryOwner huffmanImage = this.DecodeImageStream(decoder, huffmanXSize, huffmanYSize, false);
+ Span huffmanImageSpan = huffmanImage.GetSpan();
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.
- uint group = (huffmanImage[i] >> 8) & 0xffff;
- huffmanImage[i] = group;
+ uint group = (huffmanImageSpan[i] >> 8) & 0xffff;
+ huffmanImageSpan[i] = group;
if (group >= numHTreeGroupsMax)
{
numHTreeGroupsMax = (int)group + 1;
@@ -625,19 +631,26 @@ namespace SixLabors.ImageSharp.Formats.WebP
: 3;
transform.XSize = LosslessUtils.SubSampleSize(transform.XSize, bits);
transform.Bits = bits;
- uint[] colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false);
- transform.Data = LosslessUtils.ExpandColorMap((int)numColors, transform, colorMap);
+ using (IMemoryOwner colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false))
+ {
+ int finalNumColors = 1 << (8 >> transform.Bits);
+ IMemoryOwner newColorMap = this.memoryAllocator.Allocate(finalNumColors, AllocationOptions.Clean);
+ LosslessUtils.ExpandColorMap((int)numColors, colorMap.GetSpan(), newColorMap.GetSpan());
+ transform.Data = newColorMap;
+ }
+
break;
case Vp8LTransformType.PredictorTransform:
case Vp8LTransformType.CrossColorTransform:
{
transform.Bits = (int)this.bitReader.ReadBits(3) + 2;
- transform.Data = this.DecodeImageStream(
+ IMemoryOwner transformData = this.DecodeImageStream(
decoder,
LosslessUtils.SubSampleSize(transform.XSize, transform.Bits),
LosslessUtils.SubSampleSize(transform.YSize, transform.Bits),
false);
+ transform.Data = transformData;
break;
}
}
@@ -650,7 +663,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
/// The decoder holding the transformation infos.
/// The pixel data to apply the transformation.
- private void ApplyInverseTransforms(Vp8LDecoder decoder, uint[] pixelData)
+ private void ApplyInverseTransforms(Vp8LDecoder decoder, Span pixelData)
{
List transforms = decoder.Transforms;
for (int i = transforms.Count - 1; i >= 0; i--)
@@ -710,7 +723,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
return tableSpan[0].Value;
}
- private uint ReadPackedSymbols(HTreeGroup[] group, uint[] pixelData, int decodedPixels)
+ private uint ReadPackedSymbols(HTreeGroup[] group, Span pixelData, int decodedPixels)
{
uint val = (uint)(this.bitReader.PrefetchBits() & (HuffmanUtils.HuffmanPackedTableSize - 1));
HuffmanCode code = group[0].PackedTable[val];
@@ -726,18 +739,18 @@ namespace SixLabors.ImageSharp.Formats.WebP
return code.Value;
}
- private void CopyBlock(uint[] pixelData, int decodedPixels, int dist, int length)
+ private void CopyBlock(Span pixelData, int decodedPixels, int dist, int length)
{
if (dist >= length)
{
- Span src = pixelData.AsSpan(decodedPixels - dist, length);
- Span dest = pixelData.AsSpan(decodedPixels);
+ Span src = pixelData.Slice(decodedPixels - dist, length);
+ Span dest = pixelData.Slice(decodedPixels);
src.CopyTo(dest);
}
else
{
- Span src = pixelData.AsSpan(decodedPixels - dist);
- Span dest = pixelData.AsSpan(decodedPixels);
+ Span src = pixelData.Slice(decodedPixels - dist);
+ Span dest = pixelData.Slice(decodedPixels);
for (int i = 0; i < length; ++i)
{
dest[i] = src[i];
@@ -811,14 +824,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
return hCode.BitsUsed;
}
- private uint GetMetaIndex(uint[] image, int xSize, int bits, int x, int y)
+ private uint GetMetaIndex(IMemoryOwner huffmanImage, int xSize, int bits, int x, int y)
{
if (bits is 0)
{
return 0;
}
- return image[(xSize * (y >> bits)) + (x >> bits)];
+ Span huffmanImageSpan = huffmanImage.GetSpan();
+ return huffmanImageSpan[(xSize * (y >> bits)) + (x >> bits)];
}
private HTreeGroup[] GetHTreeGroupForPos(Vp8LMetadata metadata, int x, int y)