Browse Source

Remove some low hanging allocations

pull/1552/head
James Jackson-South 6 years ago
parent
commit
3488203684
  1. 2
      src/ImageSharp/Formats/WebP/BitReader/Vp8BitReader.cs
  2. 19
      src/ImageSharp/Formats/WebP/Lossless/WebPLosslessDecoder.cs
  3. 2
      src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs
  4. 21
      src/ImageSharp/Formats/WebP/Lossy/WebPLossyDecoder.cs
  5. 3
      src/ImageSharp/Formats/WebP/WebPCommonUtils.cs
  6. 3
      src/ImageSharp/Formats/WebP/WebPLookupTables.cs
  7. 11
      tests/ImageSharp.Benchmarks/Codecs/DecodeWebp.cs
  8. 8
      tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs

2
src/ImageSharp/Formats/WebP/BitReader/Vp8BitReader.cs

@ -91,6 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitReader
public uint Remaining { get; set; }
[MethodImpl(InliningOptions.ShortMethod)]
public int GetBit(int prob)
{
uint range = this.range;
@ -184,6 +185,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitReader
this.LoadNewBytes();
}
[MethodImpl(InliningOptions.ColdPath)]
private void LoadNewBytes()
{
if (this.pos < this.bufferMax)

19
src/ImageSharp/Formats/WebP/Lossless/WebPLosslessDecoder.cs

@ -381,9 +381,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
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.
@ -440,6 +443,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
WebPThrowHelper.ThrowImageFormatException("Huffman table size is zero");
}
// TODO: Avoid allocation.
hTreeGroup.HTrees.Add(huffmanTable.ToArray());
HuffmanCode huffTableZero = huffmanTable[0];
@ -472,10 +476,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
hTreeGroup.IsTrivialCode = false;
if (isTrivialLiteral)
{
uint red = hTreeGroup.HTrees[HuffIndex.Red].First().Value;
uint blue = hTreeGroup.HTrees[HuffIndex.Blue].First().Value;
uint green = hTreeGroup.HTrees[HuffIndex.Green].First().Value;
uint alpha = hTreeGroup.HTrees[HuffIndex.Alpha].First().Value;
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)
{
@ -542,7 +546,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
codeLengthCodeLengths[CodeLengthCodeOrder[i]] = (int)this.bitReader.ReadValue(3);
}
this.ReadHuffmanCodeLengths(table.ToArray(), codeLengthCodeLengths, alphabetSize, codeLengths);
this.ReadHuffmanCodeLengths(table, codeLengthCodeLengths, alphabetSize, codeLengths);
}
int size = HuffmanUtils.BuildHuffmanTable(table, HuffmanUtils.HuffmanTableBits, codeLengths, alphabetSize);
@ -550,7 +554,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
return size;
}
private void ReadHuffmanCodeLengths(HuffmanCode[] table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths)
private void ReadHuffmanCodeLengths(Span<HuffmanCode> table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths)
{
int maxSymbol;
int symbol = 0;
@ -580,7 +584,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
this.bitReader.FillBitWindow();
ulong prefetchBits = this.bitReader.PrefetchBits();
ulong idx = prefetchBits & 127;
int idx = (int)(prefetchBits & 127);
HuffmanCode huffmanCode = table[idx];
this.bitReader.AdvanceBitPosition(huffmanCode.BitsUsed);
uint codeLen = huffmanCode.Value;
@ -625,6 +629,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
var transform = new Vp8LTransform(transformType, xSize, ySize);
// Each transform is allowed to be used only once.
// TODO: No Linq, avoid 'transform' closure allocation.
if (decoder.Transforms.Any(t => t.TransformType == transform.TransformType))
{
WebPThrowHelper.ThrowImageFormatException("Each transform can only be present once");

2
src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs

@ -492,7 +492,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public static void TransformOne(Span<short> src, Span<byte> dst)
{
var tmp = new int[4 * 4];
Span<int> tmp = stackalloc int[4 * 4];
int tmpOffset = 0;
for (int srcOffset = 0; srcOffset < 4; srcOffset++)
{

21
src/ImageSharp/Formats/WebP/Lossy/WebPLossyDecoder.cs

@ -55,14 +55,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
sbyte colorSpace = (sbyte)this.bitReader.ReadValue(1);
sbyte clampType = (sbyte)this.bitReader.ReadValue(1);
var pictureHeader = new Vp8PictureHeader()
{
Width = (uint)width,
Height = (uint)height,
XScale = info.XScale,
YScale = info.YScale,
ColorSpace = colorSpace,
ClampType = clampType
};
{
Width = (uint)width,
Height = (uint)height,
XScale = info.XScale,
YScale = info.YScale,
ColorSpace = colorSpace,
ClampType = clampType
};
// Paragraph 9.3: Parse the segment header.
var proba = new Vp8Proba();
@ -135,10 +135,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
Span<byte> alphaSpan = alpha.Memory.Span;
for (int y = 0; y < height; y++)
{
// TODO: Can we use span.Length here?
int yMulWidth = y * width;
Span<TPixel> pixelRow = pixels.GetRowSpan(y);
for (int x = 0; x < width; x++)
{
// TODO: Could use cast to Bgr24/Bgra32 then set alpha.
int offset = yMulWidth + x;
int idxBgr = offset * 3;
byte b = pixelData[idxBgr];
@ -165,9 +167,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
this.ParseIntraMode(dec, mbX);
}
for (; dec.MbX < dec.MbWidth; ++dec.MbX)
while (dec.MbX < dec.MbWidth)
{
this.DecodeMacroBlock(dec, bitreader);
++dec.MbX;
}
// Prepare for next scanline.

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

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP
{
@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
n >>= 8;
}
return logValue + WebPLookupTables.LogTable8Bit[n];
return logValue + Unsafe.Add(ref MemoryMarshal.GetReference(WebPLookupTables.LogTable8Bit), (int)n);
}
}
}

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

@ -6,6 +6,7 @@ using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP
{
#pragma warning disable SA1201 // Elements should appear in the correct order
internal static class WebPLookupTables
{
public static readonly Dictionary<int, byte> Abs0;
@ -418,7 +419,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
};
// 31 ^ clz(i)
public static readonly byte[] LogTable8Bit =
public static ReadOnlySpan<byte> LogTable8Bit => new byte[]
{
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,

11
tests/ImageSharp.Benchmarks/Codecs/DecodeWebp.cs

@ -5,7 +5,7 @@ using System.IO;
using BenchmarkDotNet.Attributes;
using ImageMagick;
using SixLabors.ImageSharp.Formats.Experimental.WebP;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests;
@ -14,6 +14,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
[Config(typeof(Config.ShortClr))]
public class DecodeWebp : BenchmarkBase
{
private Configuration configuration;
private byte[] webpLossyBytes;
private byte[] webpLosslessBytes;
@ -31,6 +33,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
[GlobalSetup]
public void ReadImages()
{
this.configuration = Configuration.CreateDefaultInstance();
new WebPConfigurationModule().Configure(this.configuration);
this.webpLossyBytes ??= File.ReadAllBytes(this.TestImageLossyFullPath);
this.webpLosslessBytes ??= File.ReadAllBytes(this.TestImageLosslessFullPath);
}
@ -47,7 +52,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public int WebpLossy()
{
using var memoryStream = new MemoryStream(this.webpLossyBytes);
using var image = Image.Load<Rgba32>(memoryStream);
using var image = Image.Load<Rgba32>(this.configuration, memoryStream);
return image.Height;
}
@ -63,7 +68,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public int WebpLossless()
{
using var memoryStream = new MemoryStream(this.webpLosslessBytes);
using var image = Image.Load<Rgba32>(memoryStream);
using var image = Image.Load<Rgba32>(this.configuration, memoryStream);
return image.Height;
}

8
tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs

@ -332,5 +332,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.WebP
}
});
}
[Theory]
[WithFile(Lossless.Earth, PixelTypes.Rgba32)]
public void ProfileTestLossless<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(WebpDecoder);
}
}
}

Loading…
Cancel
Save