Browse Source

Hide Buffer<T> indexer + !! WuQuantizer review in comments !!

af/merge-core
Anton Firszov 8 years ago
parent
commit
7881fdd341
  1. 13
      src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
  2. 13
      src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
  3. 13
      src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
  4. 13
      src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
  5. 15
      src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
  6. 9
      src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
  7. 24
      src/ImageSharp.Drawing/Processors/FillProcessor.cs
  8. 4
      src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
  9. 26
      src/ImageSharp/Formats/Gif/LzwDecoder.cs
  10. 39
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  11. 4
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
  12. 22
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs
  13. 10
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs
  14. 8
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs
  15. 14
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs
  16. 4
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  17. 10
      src/ImageSharp/Memory/BasicArrayBuffer.cs
  18. 69
      src/ImageSharp/Memory/Buffer{T}.cs
  19. 4
      src/ImageSharp/Memory/MemoryManager.cs
  20. 14
      src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs
  21. 16
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
  22. 18
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
  23. 8
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  24. 1
      src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs
  25. 173
      src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs
  26. 4
      tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs
  27. 4
      tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs
  28. 4
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs
  29. 4
      tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs
  30. 4
      tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs
  31. 42
      tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs
  32. 5
      tests/ImageSharp.Benchmarks/Samplers/Glow.cs
  33. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
  34. 31
      tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs
  35. 21
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs

13
src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs

@ -122,24 +122,27 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
internal override void Apply(Span<float> scanline, int x, int y)
{
// Create a span for colors
using (var amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (var overlay = this.Target.MemoryManager.Allocate<TPixel>(scanline.Length))
using (Buffer<float> amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (Buffer<TPixel> overlay = this.Target.MemoryManager.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
int sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX;
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(sourceY);
for (int i = 0; i < scanline.Length; i++)
{
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
int sourceX = (i + offsetX) % this.xLength;
TPixel pixel = sourceRow[sourceX];
overlay[i] = pixel;
overlaySpan[i] = pixel;
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

13
src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs

@ -152,19 +152,22 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
internal override void Apply(Span<float> scanline, int x, int y)
{
int patternY = y % this.pattern.Height;
using (var amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (var overlay = this.Target.MemoryManager.Allocate<TPixel>(scanline.Length))
using (Buffer<float> amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (Buffer<TPixel> overlay = this.Target.MemoryManager.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
for (int i = 0; i < scanline.Length; i++)
{
amountBuffer[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1);
amountSpan[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1);
int patternX = (x + i) % this.pattern.Width;
overlay[i] = this.pattern[patternY, patternX];
overlaySpan[i] = this.pattern[patternY, patternX];
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

13
src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs

@ -65,21 +65,24 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors
/// <remarks>scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.</remarks>
internal virtual void Apply(Span<float> scanline, int x, int y)
{
using (var amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (var overlay = this.Target.MemoryManager.Allocate<TPixel>(scanline.Length))
using (Buffer<float> amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (Buffer<TPixel> overlay = this.Target.MemoryManager.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
for (int i = 0; i < scanline.Length; i++)
{
if (this.Options.BlendPercentage < 1)
{
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
}
overlay[i] = this[x + i, y];
overlaySpan[i] = this[x + i, y];
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

13
src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs

@ -144,22 +144,25 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y)
{
using (var amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (var overlay = this.Target.MemoryManager.Allocate<TPixel>(scanline.Length))
using (Buffer<float> amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (Buffer<TPixel> overlay = this.Target.MemoryManager.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
for (int i = 0; i < scanline.Length; i++)
{
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
int offsetX = x + i;
// no doubt this one can be optermised further but I can't imagine its
// actually being used and can probably be removed/interalised for now
overlay[i] = this[offsetX, y];
overlaySpan[i] = this[offsetX, y];
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

15
src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs

@ -62,10 +62,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
: base(source, options)
{
this.Colors = source.MemoryManager.Allocate<TPixel>(source.Width);
for (int i = 0; i < this.Colors.Length; i++)
{
this.Colors[i] = color;
}
this.Colors.Span.Fill(color);
}
/// <summary>
@ -81,7 +78,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <returns>
/// The color
/// </returns>
internal override TPixel this[int x, int y] => this.Colors[x];
internal override TPixel this[int x, int y] => this.Colors.Span[x];
/// <inheritdoc />
public override void Dispose()
@ -96,14 +93,16 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
{
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
using (var amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (Buffer<float> amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
for (int i = 0; i < scanline.Length; i++)
{
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
}
this.Blender.Blend(destinationRow, destinationRow, this.Colors, amountBuffer);
this.Blender.Blend(destinationRow, destinationRow, this.Colors.Span, amountSpan);
}
}
catch (Exception)

9
src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs

@ -84,12 +84,9 @@ namespace SixLabors.ImageSharp.Drawing.Processors
maxY = Math.Min(this.Location.Y + this.Size.Height, maxY);
int width = maxX - minX;
using (var amount = this.Image.GetConfiguration().MemoryManager.Allocate<float>(width))
using (Buffer<float> amount = this.Image.GetConfiguration().MemoryManager.Allocate<float>(width))
{
for (int i = 0; i < width; i++)
{
amount[i] = this.Alpha;
}
amount.Span.Fill(this.Alpha);
Parallel.For(
minY,
@ -99,7 +96,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors
{
Span<TPixel> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixel> foreground = targetImage.GetPixelRowSpan(y - this.Location.Y).Slice(targetX, width);
this.blender.Blend(background, background, foreground, amount);
this.blender.Blend(background, background, foreground, amount.Span);
});
}
}

24
src/ImageSharp.Drawing/Processors/FillProcessor.cs

@ -66,25 +66,25 @@ namespace SixLabors.ImageSharp.Drawing.Processors
int width = maxX - minX;
using (var amount = source.MemoryManager.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options))
using (Buffer<float> amount = source.MemoryManager.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source,
sourceRectangle,
this.options))
{
for (int i = 0; i < width; i++)
{
amount[i] = this.options.BlendPercentage;
}
amount.Span.Fill(this.options.BlendPercentage);
Parallel.For(
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
int offsetY = y - startY;
int offsetX = minX - startX;
{
int offsetY = y - startY;
int offsetX = minX - startX;
applicator.Apply(amount, offsetX, offsetY);
});
applicator.Apply(amount.Span, offsetX, offsetY);
});
}
}
}

4
src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs

@ -99,8 +99,8 @@ namespace SixLabors.ImageSharp.Drawing.Processors
using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
{
int scanlineWidth = maxX - minX;
using (FakeBuffer<float> buffer = source.MemoryManager.AllocateFake<float>(maxIntersections))
using (FakeBuffer<float> scanline = source.MemoryManager.AllocateFake<float>(scanlineWidth))
using (BasicArrayBuffer<float> buffer = source.MemoryManager.AllocateFake<float>(maxIntersections))
using (BasicArrayBuffer<float> scanline = source.MemoryManager.AllocateFake<float>(scanlineWidth))
{
bool scanlineDirty = true;
for (int y = minY; y < maxY; y++)

26
src/ImageSharp/Formats/Gif/LzwDecoder.cs

@ -115,10 +115,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
int data = 0;
int first = 0;
Span<int> prefixSpan = this.prefix.Span;
Span<int> suffixSpan = this.suffix.Span;
Span<int> pixelStackSpan = this.pixelStack.Span;
for (code = 0; code < clearCode; code++)
{
this.prefix[code] = 0;
this.suffix[code] = (byte)code;
prefixSpan[code] = 0;
suffixSpan[code] = (byte)code;
}
byte[] buffer = new byte[255];
@ -172,7 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (oldCode == NullCode)
{
this.pixelStack[top++] = this.suffix[code];
pixelStackSpan[top++] = suffixSpan[code];
oldCode = code;
first = code;
continue;
@ -181,27 +185,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
int inCode = code;
if (code == availableCode)
{
this.pixelStack[top++] = (byte)first;
pixelStackSpan[top++] = (byte)first;
code = oldCode;
}
while (code > clearCode)
{
this.pixelStack[top++] = this.suffix[code];
code = this.prefix[code];
pixelStackSpan[top++] = suffixSpan[code];
code = prefixSpan[code];
}
first = this.suffix[code];
first = suffixSpan[code];
this.pixelStack[top++] = this.suffix[code];
pixelStackSpan[top++] = suffixSpan[code];
// Fix for Gifs that have "deferred clear code" as per here :
// https://bugzilla.mozilla.org/show_bug.cgi?id=55918
if (availableCode < MaxStackSize)
{
this.prefix[availableCode] = oldCode;
this.suffix[availableCode] = first;
prefixSpan[availableCode] = oldCode;
suffixSpan[availableCode] = first;
availableCode++;
if (availableCode == codeMask + 1 && availableCode < MaxStackSize)
{
@ -217,7 +221,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
top--;
// Clear missing pixels
pixels[xyz++] = (byte)this.pixelStack[top];
pixels[xyz++] = (byte)pixelStackSpan[top];
}
}

39
src/ImageSharp/Formats/Gif/LzwEncoder.cs

@ -259,7 +259,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="stream">The output stream.</param>
private void ClearBlock(Stream stream)
{
this.ResetCodeTable(this.hsize);
this.ResetCodeTable();
this.freeEntry = this.clearCode + 2;
this.clearFlag = true;
@ -269,13 +269,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Reset the code table.
/// </summary>
/// <param name="size">The hash size.</param>
private void ResetCodeTable(int size)
private void ResetCodeTable()
{
for (int i = 0; i < size; ++i)
{
this.hashTable[i] = -1;
}
this.hashTable.Span.Fill(-1);
// Original code:
// for (int i = 0; i < size; ++i)
// {
// this.hashTable[i] = -1;
// }
}
/// <summary>
@ -317,23 +319,26 @@ namespace SixLabors.ImageSharp.Formats.Gif
hsizeReg = this.hsize;
this.ResetCodeTable(hsizeReg); // clear hash table
this.ResetCodeTable(); // clear hash table
this.Output(this.clearCode, stream);
Span<int> hashTableSpan = this.hashTable.Span;
Span<int> codeTableSpan = this.codeTable.Span;
while ((c = this.NextPixel()) != Eof)
{
fcode = (c << this.maxbits) + ent;
int i = (c << hshift) ^ ent /* = 0 */;
if (this.hashTable[i] == fcode)
if (hashTableSpan[i] == fcode)
{
ent = this.codeTable[i];
ent = codeTableSpan[i];
continue;
}
// Non-empty slot
if (this.hashTable[i] >= 0)
if (hashTableSpan[i] >= 0)
{
int disp = hsizeReg - i;
if (i == 0)
@ -348,15 +353,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
i += hsizeReg;
}
if (this.hashTable[i] == fcode)
if (hashTableSpan[i] == fcode)
{
ent = this.codeTable[i];
ent = codeTableSpan[i];
break;
}
}
while (this.hashTable[i] >= 0);
while (hashTableSpan[i] >= 0);
if (this.hashTable[i] == fcode)
if (hashTableSpan[i] == fcode)
{
continue;
}
@ -366,8 +371,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
ent = c;
if (this.freeEntry < this.maxmaxcode)
{
this.codeTable[i] = this.freeEntry++; // code -> hashtable
this.hashTable[i] = fcode;
codeTableSpan[i] = this.freeEntry++; // code -> hashtable
hashTableSpan[i] = fcode;
}
else
{

4
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs

@ -150,11 +150,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
int y = yy - this.PixelRowCounter;
var values = new ColorConverters.JpegColorConverter.ComponentValues(buffers, y);
this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer);
this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer.Span);
Span<TPixel> destRow = destination.GetPixelRowSpan(yy);
PixelOperations<TPixel>.Instance.PackFromVector4(this.rgbaBuffer, destRow, destination.Width);
PixelOperations<TPixel>.Instance.PackFromVector4(this.rgbaBuffer.Span, destRow, destination.Width);
}
}
}

22
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs

@ -14,6 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Bytes is a byte buffer, similar to a stream, except that it
/// has to be able to unread more than 1 byte, due to byte stuffing.
/// Byte stuffing is specified in section F.1.2.3.
/// TODO: Optimize buffer management inside this class!
/// </summary>
internal struct Bytes : IDisposable
{
@ -26,7 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Gets or sets the buffer.
/// buffer[i:j] are the buffered bytes read from the underlying
/// stream that haven't yet been passed further on.
/// TODO: Do we really need buffer here? Might be an optimiziation opportunity.
/// </summary>
public IManagedByteBuffer Buffer;
@ -88,7 +88,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
// Take the fast path if bytes.buf contains at least two bytes.
if (this.I + 2 <= this.J)
{
x = this.BufferAsInt[this.I];
Span<int> bufferSpan = this.BufferAsInt.Span;
x = bufferSpan[this.I];
this.I++;
this.UnreadableBytes = 1;
if (x != OrigJpegConstants.Markers.XFFInt)
@ -96,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
return OrigDecoderErrorCode.NoError;
}
if (this.BufferAsInt[this.I] != 0x00)
if (bufferSpan[this.I] != 0x00)
{
return OrigDecoderErrorCode.MissingFF00;
}
@ -196,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
}
}
result = this.BufferAsInt[this.I];
result = this.BufferAsInt.Span[this.I];
this.I++;
this.UnreadableBytes = 0;
return errorCode;
@ -230,20 +231,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
DecoderThrowHelper.ThrowImageFormatException.FillCalledWhenUnreadBytesExist();
}
Span<byte> bufferSpan = this.Buffer.Span;
Span<byte> byteSpan = this.Buffer.Span;
// Move the last 2 bytes to the start of the buffer, in case we need
// to call UnreadByteStuffedByte.
if (this.J > 2)
{
bufferSpan[0] = bufferSpan[this.J - 2];
bufferSpan[1] = bufferSpan[this.J - 1];
byteSpan[0] = byteSpan[this.J - 2];
byteSpan[1] = byteSpan[this.J - 1];
this.I = 2;
this.J = 2;
}
// Fill in the rest of the buffer.
int n = inputStream.Read(this.Buffer.Array, this.J, bufferSpan.Length - this.J);
int n = inputStream.Read(this.Buffer.Array, this.J, byteSpan.Length - this.J);
if (n == 0)
{
return OrigDecoderErrorCode.UnexpectedEndOfStream;
@ -251,9 +252,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.J += n;
for (int i = 0; i < bufferSpan.Length; i++)
Span<int> intSpan = this.BufferAsInt.Span;
for (int i = 0; i < byteSpan.Length; i++)
{
this.BufferAsInt[i] = bufferSpan[i];
intSpan[i] = byteSpan[i];
}
return OrigDecoderErrorCode.NoError;

10
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs

@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary>
internal struct PdfJsHuffmanTable : IDisposable
{
private FakeBuffer<short> lookahead;
private FakeBuffer<short> valOffset;
private FakeBuffer<long> maxcode;
private BasicArrayBuffer<short> lookahead;
private BasicArrayBuffer<short> valOffset;
private BasicArrayBuffer<long> maxcode;
private IManagedByteBuffer huffval;
/// <summary>
@ -30,8 +30,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.valOffset = memoryManager.AllocateFake<short>(18);
this.maxcode = memoryManager.AllocateFake<long>(18);
using (FakeBuffer<short> huffsize = memoryManager.AllocateFake<short>(257))
using (FakeBuffer<short> huffcode = memoryManager.AllocateFake<short>(257))
using (BasicArrayBuffer<short> huffsize = memoryManager.AllocateFake<short>(257))
using (BasicArrayBuffer<short> huffcode = memoryManager.AllocateFake<short>(257))
{
GenerateSizeTable(lengths, huffsize);
GenerateCodeTable(huffsize, huffcode);

8
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs

@ -74,18 +74,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
var scale = new Vector2(this.imageWidth / (float)width, this.imageHeight / (float)height);
this.componentData = this.memoryManager.Allocate<byte>(width * height * numberOfComponents);
Span<byte> componentDataSpan = this.componentData;
Span<byte> componentDataSpan = this.componentData.Span;
const uint Mask3Lsb = 0xFFFFFFF8; // Used to clear the 3 LSBs
using (var xScaleBlockOffset = this.memoryManager.Allocate<int>(width))
using (IBuffer<int> xScaleBlockOffset = this.memoryManager.Allocate<int>(width))
{
Span<int> xScaleBlockOffsetSpan = xScaleBlockOffset;
Span<int> xScaleBlockOffsetSpan = xScaleBlockOffset.Span;
for (int i = 0; i < numberOfComponents; i++)
{
ref PdfJsComponent component = ref components.Components[i];
Vector2 componentScale = component.Scale * scale;
int offset = i;
Span<short> output = component.Output;
Span<short> output = component.Output.Span;
int blocksPerScanline = (component.BlocksPerLine + 1) << 3;
// Precalculate the xScaleBlockOffset

14
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs

@ -707,6 +707,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBaseline(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, Stream stream)
{
Span<short> blockDataSpan = component.BlockData.Span;
int t = this.DecodeHuffman(ref dcHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
@ -714,7 +716,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream);
component.BlockData[offset] = (short)(component.Pred += diff);
blockDataSpan[offset] = (short)(component.Pred += diff);
int k = 1;
while (k < 64)
@ -748,7 +750,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
byte z = PdfJsQuantizationTables.DctZigZag[k];
short re = (short)this.ReceiveAndExtend(s, stream);
component.BlockData[offset + z] = re;
blockDataSpan[offset + z] = re;
k++;
}
}
@ -756,6 +758,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeDCFirst(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, Stream stream)
{
Span<short> blockDataSpan = component.BlockData.Span;
int t = this.DecodeHuffman(ref dcHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
@ -763,19 +767,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream) << this.successiveState;
component.BlockData[offset] = (short)(component.Pred += diff);
blockDataSpan[offset] = (short)(component.Pred += diff);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeDCSuccessive(PdfJsFrameComponent component, int offset, Stream stream)
{
Span<short> blockDataSpan = component.BlockData.Span;
int bit = this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
component.BlockData[offset] |= (short)(bit << this.successiveState);
blockDataSpan[offset] |= (short)(bit << this.successiveState);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

4
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

@ -790,14 +790,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
using (Buffer<short> multiplicationBuffer = this.configuration.MemoryManager.Allocate<short>(64, true))
{
Span<short> quantizationTable = this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex);
Span<short> computationBufferSpan = computationBuffer;
Span<short> computationBufferSpan = computationBuffer.Span;
// For AA&N IDCT method, multiplier are equal to quantization
// coefficients scaled by scalefactor[row]*scalefactor[col], where
// scalefactor[0] = 1
// scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7
// For integer operation, the multiplier table is to be scaled by 12.
Span<short> multiplierSpan = multiplicationBuffer;
Span<short> multiplierSpan = multiplicationBuffer.Span;
// for (int i = 0; i < 64; i++)
// {

10
src/ImageSharp/Memory/FakeBuffer.cs → src/ImageSharp/Memory/BasicArrayBuffer.cs

@ -4,12 +4,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Memory
{
/// <summary>
/// Temporal workaround providing a "Buffer" based on a generic array without the 'Unsafe.As()' hackery.
/// Exposes an array through the <see cref="IBuffer{T}"/> interface.
/// </summary>
internal class FakeBuffer<T> : IBuffer<T>
internal class BasicArrayBuffer<T> : IBuffer<T>
where T : struct
{
public FakeBuffer(T[] array)
public BasicArrayBuffer(T[] array)
{
this.Array = array;
}
@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Memory
/// </summary>
/// <param name="buffer">The <see cref="Buffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(FakeBuffer<T> buffer)
public static implicit operator ReadOnlySpan<T>(BasicArrayBuffer<T> buffer)
{
return new ReadOnlySpan<T>(buffer.Array, 0, buffer.Length);
}
@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Memory
/// </summary>
/// <param name="buffer">The <see cref="Buffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Span<T>(FakeBuffer<T> buffer)
public static implicit operator Span<T>(BasicArrayBuffer<T> buffer)
{
return new Span<T>(buffer.Array, 0, buffer.Length);
}

69
src/ImageSharp/Memory/Buffer{T}.cs

@ -38,11 +38,6 @@ namespace SixLabors.ImageSharp.Memory
this.memoryManager = memoryManager;
}
/// <summary>
/// Gets a value indicating whether this <see cref="Buffer{T}"/> instance is disposed, or has lost ownership of <see cref="array"/>.
/// </summary>
public bool IsDisposedOrLostArrayOwnership { get; private set; }
/// <summary>
/// Gets the count of "relevant" elements. It's usually smaller than 'Array.Length' when <see cref="array"/> is pooled.
/// </summary>
@ -51,44 +46,7 @@ namespace SixLabors.ImageSharp.Memory
/// <summary>
/// Gets a <see cref="Span{T}"/> to the backing buffer.
/// </summary>
public Span<T> Span => this;
/// <summary>
/// Returns a reference to specified element of the buffer.
/// </summary>
/// <param name="index">The index</param>
/// <returns>The reference to the specified element</returns>
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
DebugGuard.MustBeLessThan(index, this.Length, nameof(index));
Span<T> span = this.Span;
return ref span[index];
}
}
/// <summary>
/// Converts <see cref="Buffer{T}"/> to an <see cref="ReadOnlySpan{T}"/>.
/// </summary>
/// <param name="buffer">The <see cref="Buffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(Buffer<T> buffer)
{
return new ReadOnlySpan<T>(buffer.array, 0, buffer.Length);
}
/// <summary>
/// Converts <see cref="Buffer{T}"/> to an <see cref="Span{T}"/>.
/// </summary>
/// <param name="buffer">The <see cref="Buffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Span<T>(Buffer<T> buffer)
{
return new Span<T>(buffer.array, 0, buffer.Length);
}
public Span<T> Span => new Span<T>(this.array, 0, this.Length);
/// <summary>
/// Disposes the <see cref="Buffer{T}"/> instance by unpinning the array, and returning the pooled buffer when necessary.
@ -96,13 +54,11 @@ namespace SixLabors.ImageSharp.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (this.IsDisposedOrLostArrayOwnership)
if (this.array == null)
{
return;
}
this.IsDisposedOrLostArrayOwnership = true;
this.memoryManager?.Release(this);
this.memoryManager = null;
@ -112,27 +68,6 @@ namespace SixLabors.ImageSharp.Memory
GC.SuppressFinalize(this);
}
/// <summary>
/// Unpins <see cref="array"/> and makes the object "quasi-disposed" so the array is no longer owned by this object.
/// If <see cref="array"/> is rented, it's the callers responsibility to return it to it's pool.
/// </summary>
/// <returns>The unpinned <see cref="array"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] TakeArrayOwnership()
{
if (this.IsDisposedOrLostArrayOwnership)
{
throw new InvalidOperationException(
"TakeArrayOwnership() is invalid: either Buffer<T> is disposed or TakeArrayOwnership() has been called multiple times!");
}
this.IsDisposedOrLostArrayOwnership = true;
T[] a = this.array;
this.array = null;
this.memoryManager = null;
return a;
}
/// <summary>
/// TODO: Refactor this
/// </summary>

4
src/ImageSharp/Memory/MemoryManager.cs

@ -37,10 +37,10 @@ namespace SixLabors.ImageSharp.Memory
/// Temporal workaround. A method providing a "Buffer" based on a generic array without the 'Unsafe.As()' hackery.
/// Should be replaced with 'Allocate()' as soon as SixLabors.Shapes has Span-based API-s!
/// </summary>
internal FakeBuffer<T> AllocateFake<T>(int length, bool dummy = false)
internal BasicArrayBuffer<T> AllocateFake<T>(int length, bool dummy = false)
where T : struct
{
return new FakeBuffer<T>(new T[length]);
return new BasicArrayBuffer<T>(new T[length]);
}
}
}

14
src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs

@ -71,13 +71,17 @@ namespace SixLabors.ImageSharp.Processing.Processors
int width = maxX - minX;
using (var colors = this.memoryManager.Allocate<TPixel>(width))
using (var amount = this.memoryManager.Allocate<float>(width))
using (Buffer<TPixel> colors = this.memoryManager.Allocate<TPixel>(width))
using (Buffer<float> amount = this.memoryManager.Allocate<float>(width))
{
// Be careful! Do not capture colorSpan & amountSpan in the lambda below!
Span<TPixel> colorSpan = colors.Span;
Span<float> amountSpan = amount.Span;
for (int i = 0; i < width; i++)
{
colors[i] = this.Value;
amount[i] = this.options.BlendPercentage;
colorSpan[i] = this.Value;
amountSpan[i] = this.options.BlendPercentage;
}
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(this.options.BlenderMode);
@ -90,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Span<TPixel> destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width);
// This switched color & destination in the 2nd and 3rd places because we are applying the target colour under the current one
blender.Blend(destination, colors, destination, amount);
blender.Blend(destination, colors.Span, destination, amount.Span);
});
}
}

16
src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs

@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
TPixel glowColor = this.GlowColor;
Vector2 centre = Rectangle.Center(sourceRectangle);
var finalRadius = this.Radius.Calculate(source.Size());
float finalRadius = this.Radius.Calculate(source.Size());
float maxDistance = finalRadius > 0 ? MathF.Min(finalRadius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F;
@ -87,11 +87,14 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
int width = maxX - minX;
using (var rowColors = this.memoryManager.Allocate<TPixel>(width))
using (IBuffer<TPixel> rowColors = this.memoryManager.Allocate<TPixel>(width))
{
// Be careful! Do not capture rowColorsSpan in the lambda below!
Span<TPixel> rowColorsSpan = rowColors.Span;
for (int i = 0; i < width; i++)
{
rowColors[i] = glowColor;
rowColorsSpan[i] = glowColor;
}
Parallel.For(
@ -100,19 +103,20 @@ namespace SixLabors.ImageSharp.Processing.Processors
configuration.ParallelOptions,
y =>
{
using (var amounts = this.memoryManager.Allocate<float>(width))
using (IBuffer<float> amounts = this.memoryManager.Allocate<float>(width))
{
Span<float> amountsSpan = amounts.Span;
int offsetY = y - startY;
int offsetX = minX - startX;
for (int i = 0; i < width; i++)
{
float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY));
amounts[i] = (this.options.BlendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1);
amountsSpan[i] = (this.options.BlendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1);
}
Span<TPixel> destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width);
this.blender.Blend(destination, destination, rowColors, amounts);
this.blender.Blend(destination, destination, rowColors.Span, amountsSpan);
}
});
}

18
src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs

@ -86,8 +86,8 @@ namespace SixLabors.ImageSharp.Processing.Processors
TPixel vignetteColor = this.VignetteColor;
Vector2 centre = Rectangle.Center(sourceRectangle);
var finalradiusX = this.RadiusX.Calculate(source.Size());
var finalradiusY = this.RadiusY.Calculate(source.Size());
float finalradiusX = this.RadiusX.Calculate(source.Size());
float finalradiusY = this.RadiusY.Calculate(source.Size());
float rX = finalradiusX > 0 ? MathF.Min(finalradiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F;
float rY = finalradiusY > 0 ? MathF.Min(finalradiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F;
float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY));
@ -110,11 +110,14 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
int width = maxX - minX;
using (var rowColors = this.memoryManager.Allocate<TPixel>(width))
using (IBuffer<TPixel> rowColors = this.memoryManager.Allocate<TPixel>(width))
{
// Be careful! Do not capture rowColorsSpan in the lambda below!
Span<TPixel> rowColorsSpan = rowColors.Span;
for (int i = 0; i < width; i++)
{
rowColors[i] = vignetteColor;
rowColorsSpan[i] = vignetteColor;
}
Parallel.For(
@ -123,19 +126,20 @@ namespace SixLabors.ImageSharp.Processing.Processors
configuration.ParallelOptions,
y =>
{
using (var amounts = this.memoryManager.Allocate<float>(width))
using (IBuffer<float> amounts = this.memoryManager.Allocate<float>(width))
{
Span<float> amountsSpan = amounts.Span;
int offsetY = y - startY;
int offsetX = minX - startX;
for (int i = 0; i < width; i++)
{
float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY));
amounts[i] = (this.options.BlendPercentage * (.9F * (distance / maxDistance))).Clamp(0, 1);
amountsSpan[i] = (this.options.BlendPercentage * (.9F * (distance / maxDistance))).Clamp(0, 1);
}
Span<TPixel> destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width);
this.blender.Blend(destination, destination, rowColors, amounts);
this.blender.Blend(destination, destination, rowColors.Span, amountsSpan);
}
});
}

8
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -135,18 +135,18 @@ namespace SixLabors.ImageSharp.Processing.Processors
y =>
{
// TODO: Without Parallel.For() this buffer object could be reused:
using (var tempRowBuffer = this.MemoryManager.Allocate<Vector4>(source.Width))
using (IBuffer<Vector4> tempRowBuffer = this.MemoryManager.Allocate<Vector4>(source.Width))
{
Span<Vector4> firstPassRow = firstPassPixels.GetRowSpan(y);
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(sourceRow, tempRowBuffer, sourceRow.Length);
PixelOperations<TPixel>.Instance.ToVector4(sourceRow, tempRowBuffer.Span, sourceRow.Length);
if (this.Compand)
{
for (int x = minX; x < maxX; x++)
{
WeightsWindow window = this.HorizontalWeights.Weights[x - startX];
firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX);
firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer.Span, sourceX);
}
}
else
@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
for (int x = minX; x < maxX; x++)
{
WeightsWindow window = this.HorizontalWeights.Weights[x - startX];
firstPassRow[x] = window.ComputeWeightedRowSum(tempRowBuffer, sourceX);
firstPassRow[x] = window.ComputeWeightedRowSum(tempRowBuffer.Span, sourceX);
}
}
}

1
src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs

@ -115,6 +115,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base
/// <summary>
/// Override this to process the pixel in the first pass of the algorithm
/// TODO: We really should do this on a per-row basis! Shouldn't we internalize this method?
/// </summary>
/// <param name="pixel">The pixel to quantize</param>
/// <remarks>

173
src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs

@ -36,6 +36,14 @@ namespace SixLabors.ImageSharp.Quantizers
public class WuQuantizer<TPixel> : QuantizerBase<TPixel>
where TPixel : struct, IPixel<TPixel>
{
// TODO: The WuQuantizer<TPixel> code is rising several questions:
// - Do we really need to ALWAYS allocate the whole table of size TableLength? (~ 2471625 * sizeof(long) * 5 bytes )
// - Isn't an AOS ("array of structures") layout more efficient & more readable than SOA ("structure of arrays") for this particular use case?
// (T, R, G, B, A, M2) could be grouped together!
// - There are per-pixel virtual calls in InitialQuantizePixel, why not do it on a per-row basis?
// - It's a frequently used class, we need tests! (So we can optimize safely.) There are tests in the original!!! We should just adopt them!
// https://github.com/JeremyAnsel/JeremyAnsel.ColorQuant/blob/master/JeremyAnsel.ColorQuant/JeremyAnsel.ColorQuant.Tests/WuColorQuantizerTests.cs
/// <summary>
/// The index bits.
/// </summary>
@ -69,37 +77,37 @@ namespace SixLabors.ImageSharp.Quantizers
/// <summary>
/// Moment of <c>P(c)</c>.
/// </summary>
private Buffer<long> vwt;
private IBuffer<long> vwt;
/// <summary>
/// Moment of <c>r*P(c)</c>.
/// </summary>
private Buffer<long> vmr;
private IBuffer<long> vmr;
/// <summary>
/// Moment of <c>g*P(c)</c>.
/// </summary>
private Buffer<long> vmg;
private IBuffer<long> vmg;
/// <summary>
/// Moment of <c>b*P(c)</c>.
/// </summary>
private Buffer<long> vmb;
private IBuffer<long> vmb;
/// <summary>
/// Moment of <c>a*P(c)</c>.
/// </summary>
private Buffer<long> vma;
private IBuffer<long> vma;
/// <summary>
/// Moment of <c>c^2*P(c)</c>.
/// </summary>
private Buffer<float> m2;
private IBuffer<float> m2;
/// <summary>
/// Color space tag.
/// </summary>
private Buffer<byte> tag;
private IBuffer<byte> tag;
/// <summary>
/// Maximum allowed color depth
@ -153,23 +161,16 @@ namespace SixLabors.ImageSharp.Quantizers
}
finally
{
this.DisposeBuffer(ref this.vwt);
this.DisposeBuffer(ref this.vmr);
this.DisposeBuffer(ref this.vmg);
this.DisposeBuffer(ref this.vmb);
this.DisposeBuffer(ref this.vma);
this.DisposeBuffer(ref this.m2);
this.DisposeBuffer(ref this.tag);
this.vwt.Dispose();
this.vmr.Dispose();
this.vmg.Dispose();
this.vmb.Dispose();
this.vma.Dispose();
this.m2.Dispose();
this.tag.Dispose();
}
}
private void DisposeBuffer<T>(ref Buffer<T> buffer)
where T : struct
{
buffer?.Dispose();
buffer = null;
}
/// <inheritdoc/>
protected override TPixel[] GetPalette()
{
@ -213,14 +214,21 @@ namespace SixLabors.ImageSharp.Quantizers
int index = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1);
this.vwt[index]++;
this.vmr[index] += rgba.R;
this.vmg[index] += rgba.G;
this.vmb[index] += rgba.B;
this.vma[index] += rgba.A;
Span<long> vwtSpan = this.vwt.Span;
Span<long> vmrSpan = this.vmr.Span;
Span<long> vmgSpan = this.vmg.Span;
Span<long> vmbSpan = this.vmb.Span;
Span<long> vmaSpan = this.vma.Span;
Span<float> m2Span = this.m2.Span;
vwtSpan[index]++;
vmrSpan[index] += rgba.R;
vmgSpan[index] += rgba.G;
vmbSpan[index] += rgba.B;
vmaSpan[index] += rgba.A;
var vector = new Vector4(rgba.R, rgba.G, rgba.B, rgba.A);
this.m2[index] += Vector4.Dot(vector, vector);
m2Span[index] += Vector4.Dot(vector, vector);
}
/// <inheritdoc/>
@ -458,6 +466,13 @@ namespace SixLabors.ImageSharp.Quantizers
/// </summary>
private void Get3DMoments(MemoryManager memoryManager)
{
Span<long> vwtSpan = this.vwt.Span;
Span<long> vmrSpan = this.vmr.Span;
Span<long> vmgSpan = this.vmg.Span;
Span<long> vmbSpan = this.vmb.Span;
Span<long> vmaSpan = this.vma.Span;
Span<float> m2Span = this.m2.Span;
using (Buffer<long> volume = memoryManager.Allocate<long>(IndexCount * IndexAlphaCount))
using (Buffer<long> volumeR = memoryManager.Allocate<long>(IndexCount * IndexAlphaCount))
using (Buffer<long> volumeG = memoryManager.Allocate<long>(IndexCount * IndexAlphaCount))
@ -472,6 +487,20 @@ namespace SixLabors.ImageSharp.Quantizers
using (Buffer<long> areaA = memoryManager.Allocate<long>(IndexAlphaCount))
using (Buffer<float> area2 = memoryManager.Allocate<float>(IndexAlphaCount))
{
Span<long> volumeSpan = volume.Span;
Span<long> volumeRSpan = volumeR.Span;
Span<long> volumeGSpan = volumeG.Span;
Span<long> volumeBSpan = volumeB.Span;
Span<long> volumeASpan = volumeA.Span;
Span<float> volume2Span = volume2.Span;
Span<long> areaSpan = area.Span;
Span<long> areaRSpan = areaR.Span;
Span<long> areaGSpan = areaG.Span;
Span<long> areaBSpan = areaB.Span;
Span<long> areaASpan = areaA.Span;
Span<float> area2Span = area2.Span;
for (int r = 1; r < IndexCount; r++)
{
volume.Clear();
@ -503,37 +532,37 @@ namespace SixLabors.ImageSharp.Quantizers
{
int ind1 = GetPaletteIndex(r, g, b, a);
line += this.vwt[ind1];
lineR += this.vmr[ind1];
lineG += this.vmg[ind1];
lineB += this.vmb[ind1];
lineA += this.vma[ind1];
line2 += this.m2[ind1];
line += vwtSpan[ind1];
lineR += vmrSpan[ind1];
lineG += vmgSpan[ind1];
lineB += vmbSpan[ind1];
lineA += vmaSpan[ind1];
line2 += m2Span[ind1];
area[a] += line;
areaR[a] += lineR;
areaG[a] += lineG;
areaB[a] += lineB;
areaA[a] += lineA;
area2[a] += line2;
areaSpan[a] += line;
areaRSpan[a] += lineR;
areaGSpan[a] += lineG;
areaBSpan[a] += lineB;
areaASpan[a] += lineA;
area2Span[a] += line2;
int inv = (b * IndexAlphaCount) + a;
volume[inv] += area[a];
volumeR[inv] += areaR[a];
volumeG[inv] += areaG[a];
volumeB[inv] += areaB[a];
volumeA[inv] += areaA[a];
volume2[inv] += area2[a];
volumeSpan[inv] += areaSpan[a];
volumeRSpan[inv] += areaRSpan[a];
volumeGSpan[inv] += areaGSpan[a];
volumeBSpan[inv] += areaBSpan[a];
volumeASpan[inv] += areaASpan[a];
volume2Span[inv] += area2Span[a];
int ind2 = ind1 - GetPaletteIndex(1, 0, 0, 0);
this.vwt[ind1] = this.vwt[ind2] + volume[inv];
this.vmr[ind1] = this.vmr[ind2] + volumeR[inv];
this.vmg[ind1] = this.vmg[ind2] + volumeG[inv];
this.vmb[ind1] = this.vmb[ind2] + volumeB[inv];
this.vma[ind1] = this.vma[ind2] + volumeA[inv];
this.m2[ind1] = this.m2[ind2] + volume2[inv];
vwtSpan[ind1] = vwtSpan[ind2] + volumeSpan[inv];
vmrSpan[ind1] = vmrSpan[ind2] + volumeRSpan[inv];
vmgSpan[ind1] = vmgSpan[ind2] + volumeGSpan[inv];
vmbSpan[ind1] = vmbSpan[ind2] + volumeBSpan[inv];
vmaSpan[ind1] = vmaSpan[ind2] + volumeASpan[inv];
m2Span[ind1] = m2Span[ind2] + volume2Span[inv];
}
}
}
@ -553,23 +582,25 @@ namespace SixLabors.ImageSharp.Quantizers
float db = Volume(ref cube, this.vmb.Span);
float da = Volume(ref cube, this.vma.Span);
Span<float> m2Span = this.m2.Span;
float xx =
this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)]
- this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)]
- this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)]
+ this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
- this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)]
+ this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
+ this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
- this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
- this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)]
+ this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
+ this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
- this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
+ this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)]
- this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
- this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
+ this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)]
- m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)]
- m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)]
+ m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
- m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)]
+ m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
+ m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
- m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
- m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)]
+ m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
+ m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
- m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
+ m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)]
- m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
- m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
+ m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
var vector = new Vector4(dr, dg, db, da);
return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.Span));
@ -742,6 +773,8 @@ namespace SixLabors.ImageSharp.Quantizers
/// <param name="label">A label.</param>
private void Mark(ref Box cube, byte label)
{
Span<byte> tagSpan = this.tag.Span;
for (int r = cube.R0 + 1; r <= cube.R1; r++)
{
for (int g = cube.G0 + 1; g <= cube.G1; g++)
@ -750,7 +783,7 @@ namespace SixLabors.ImageSharp.Quantizers
{
for (int a = cube.A0 + 1; a <= cube.A1; a++)
{
this.tag[GetPaletteIndex(r, g, b, a)] = label;
tagSpan[GetPaletteIndex(r, g, b, a)] = label;
}
}
}
@ -833,7 +866,9 @@ namespace SixLabors.ImageSharp.Quantizers
int b = rgba.B >> (8 - IndexBits);
int a = rgba.A >> (8 - IndexAlphaBits);
return this.tag[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)];
Span<byte> tagSpan = this.tag.Span;
return tagSpan[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)];
}
}
}

4
tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs

@ -49,13 +49,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk
[Benchmark]
public void CommonBulk()
{
new PixelOperations<TPixel>().PackFromVector4(this.source, this.destination, this.Count);
new PixelOperations<TPixel>().PackFromVector4(this.source.Span, this.destination.Span, this.Count);
}
[Benchmark]
public void OptimizedBulk()
{
PixelOperations<TPixel>.Instance.PackFromVector4(this.source, this.destination, this.Count);
PixelOperations<TPixel>.Instance.PackFromVector4(this.source.Span, this.destination.Span, this.Count);
}
}

4
tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs

@ -50,13 +50,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk
[Benchmark]
public void CommonBulk()
{
new PixelOperations<TPixel>().PackFromRgba32Bytes(this.source, this.destination, this.Count);
new PixelOperations<TPixel>().PackFromRgba32Bytes(this.source.Span, this.destination.Span, this.Count);
}
[Benchmark]
public void OptimizedBulk()
{
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(this.source, this.destination, this.Count);
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(this.source.Span, this.destination.Span, this.Count);
}
}

4
tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs

@ -49,13 +49,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk
[Benchmark]
public void CommonBulk()
{
new PixelOperations<TPixel>().ToVector4(this.source, this.destination, this.Count);
new PixelOperations<TPixel>().ToVector4(this.source.Span, this.destination.Span, this.Count);
}
[Benchmark]
public void OptimizedBulk()
{
PixelOperations<TPixel>.Instance.ToVector4(this.source, this.destination, this.Count);
PixelOperations<TPixel>.Instance.ToVector4(this.source.Span, this.destination.Span, this.Count);
}
}

4
tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs

@ -55,13 +55,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk
[Benchmark]
public void CommonBulk()
{
new PixelOperations<TPixel>().ToRgb24Bytes(this.source, this.destination, this.Count);
new PixelOperations<TPixel>().ToRgb24Bytes(this.source.Span, this.destination.Span, this.Count);
}
[Benchmark]
public void OptimizedBulk()
{
PixelOperations<TPixel>.Instance.ToRgb24Bytes(this.source, this.destination, this.Count);
PixelOperations<TPixel>.Instance.ToRgb24Bytes(this.source.Span, this.destination.Span, this.Count);
}
}

4
tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs

@ -58,13 +58,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk
[Benchmark]
public void CommonBulk()
{
new PixelOperations<TPixel>().ToRgba32Bytes(this.source, this.destination, this.Count);
new PixelOperations<TPixel>().ToRgba32Bytes(this.source.Span, this.destination.Span, this.Count);
}
[Benchmark]
public void OptimizedBulk()
{
PixelOperations<TPixel>.Instance.ToRgba32Bytes(this.source, this.destination, this.Count);
PixelOperations<TPixel>.Instance.ToRgba32Bytes(this.source.Span, this.destination.Span, this.Count);
}
}

42
tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs

@ -59,21 +59,20 @@ namespace SixLabors.ImageSharp.Benchmarks
{
using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
{
Buffer<float> amounts = Configuration.Default.MemoryManager.Allocate<float>(image.Width);
for (int x = 0; x < image.Width; x++)
{
amounts[x] = 1;
}
using (PixelAccessor<Rgba32> pixels = image.Lock())
using (Buffer<float> amounts = Configuration.Default.MemoryManager.Allocate<float>(image.Width))
{
for (int y = 0; y < image.Height; y++)
amounts.Span.Fill(1);
using (PixelAccessor<Rgba32> pixels = image.Lock())
{
Span<Rgba32> span = pixels.GetRowSpan(y);
BulkVectorConvert(span, span, span, amounts);
for (int y = 0; y < image.Height; y++)
{
Span<Rgba32> span = pixels.GetRowSpan(y);
this.BulkVectorConvert(span, span, span, amounts.Span);
}
}
return new CoreSize(image.Width, image.Height);
}
return new CoreSize(image.Width, image.Height);
}
}
@ -82,21 +81,20 @@ namespace SixLabors.ImageSharp.Benchmarks
{
using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
{
Buffer<float> amounts = Configuration.Default.MemoryManager.Allocate<float>(image.Width);
for (int x = 0; x < image.Width; x++)
using (Buffer<float> amounts = Configuration.Default.MemoryManager.Allocate<float>(image.Width))
{
amounts[x] = 1;
}
using (PixelAccessor<Rgba32> pixels = image.Lock())
{
for (int y = 0; y < image.Height; y++)
amounts.Span.Fill(1);
using (PixelAccessor<Rgba32> pixels = image.Lock())
{
Span<Rgba32> span = pixels.GetRowSpan(y);
BulkPixelConvert(span, span, span, amounts);
for (int y = 0; y < image.Height; y++)
{
Span<Rgba32> span = pixels.GetRowSpan(y);
this.BulkPixelConvert(span, span, span, amounts.Span);
}
}
return new CoreSize(image.Width, image.Height);
}
return new CoreSize(image.Width, image.Height);
}
}
}

5
tests/ImageSharp.Benchmarks/Samplers/Glow.cs

@ -106,10 +106,7 @@ namespace SixLabors.ImageSharp.Benchmarks
using (Buffer<TPixel> rowColors = Configuration.Default.MemoryManager.Allocate<TPixel>(width))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
for (int i = 0; i < width; i++)
{
rowColors[i] = glowColor;
}
rowColors.Span.Fill(glowColor);
Parallel.For(
minY,

2
tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs

@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
// no need to dispose when buffer is not array owner
buffers[i] = new Buffer2D<float>(new FakeBuffer<float>(values), values.Length, 1);
buffers[i] = new Buffer2D<float>(new BasicArrayBuffer<float>(values), values.Length, 1);
}
return new JpegColorConverter.ComponentValues(buffers, 0);
}

31
tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs

@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation(
source,
expected,
(s, d) => ImageSharp.Rgba32.PixelOperations.ToVector4SimdAligned(s, d, 64)
(s, d) => ImageSharp.Rgba32.PixelOperations.ToVector4SimdAligned(s, d.Span, 64)
);
}
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
times,
() =>
{
PixelOperations<ImageSharp.Rgba32>.Instance.ToVector4(source, dest, count);
PixelOperations<ImageSharp.Rgba32>.Instance.ToVector4(source.Span, dest.Span, count);
});
}
}
@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation(
source,
expected,
(s, d) => Operations.PackFromVector4(s, d, count)
(s, d) => Operations.PackFromVector4(s, d.Span, count)
);
}
@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation(
source,
expected,
(s, d) => Operations.ToVector4(s, d, count)
(s, d) => Operations.ToVector4(s, d.Span, count)
);
}
@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation(
source,
expected,
(s, d) => Operations.PackFromRgb24Bytes(s, d, count)
(s, d) => Operations.PackFromRgb24Bytes(s, d.Span, count)
);
}
@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation(
source,
expected,
(s, d) => Operations.ToRgb24Bytes(s, d, count)
(s, d) => Operations.ToRgb24Bytes(s, d.Span, count)
);
}
@ -208,7 +208,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation(
source,
expected,
(s, d) => Operations.PackFromRgba32Bytes(s, d, count)
(s, d) => Operations.PackFromRgba32Bytes(s, d.Span, count)
);
}
@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation(
source,
expected,
(s, d) => Operations.ToRgba32Bytes(s, d, count)
(s, d) => Operations.ToRgba32Bytes(s, d.Span, count)
);
}
@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation(
source,
expected,
(s, d) => Operations.PackFromBgr24Bytes(s, d, count)
(s, d) => Operations.PackFromBgr24Bytes(s, d.Span, count)
);
}
@ -278,7 +278,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation(
source,
expected,
(s, d) => Operations.ToBgr24Bytes(s, d, count)
(s, d) => Operations.ToBgr24Bytes(s, d.Span, count)
);
}
@ -299,7 +299,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation(
source,
expected,
(s, d) => Operations.PackFromBgra32Bytes(s, d, count)
(s, d) => Operations.PackFromBgra32Bytes(s, d.Span, count)
);
}
@ -324,7 +324,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation(
source,
expected,
(s, d) => Operations.ToBgra32Bytes(s, d, count)
(s, d) => Operations.ToBgra32Bytes(s, d.Span, count)
);
}
@ -336,10 +336,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
public TSource[] SourceBuffer { get; }
public Buffer<TDest> ActualDestBuffer { get; }
public TDest[] ExpectedDestBuffer { get; }
public Span<TSource> Source => this.SourceBuffer;
public Span<TDest> ActualDest => this.ActualDestBuffer;
public TestBuffers(TSource[] source, TDest[] expectedDest)
{
this.SourceBuffer = source;
@ -386,7 +383,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
internal static void TestOperation<TSource, TDest>(
TSource[] source,
TDest[] expected,
Action<TSource[], Buffer<TDest>> action)
Action<TSource[], IBuffer<TDest>> action)
where TSource : struct
where TDest : struct
{

21
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs

@ -19,13 +19,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
int length = source.Length;
Guard.MustBeSizedAtLeast(dest, length, nameof(dest));
using (var rgbaBuffer = Configuration.Default.MemoryManager.Allocate<Rgba32>(length))
using (Buffer<Rgba32> rgbaBuffer = Configuration.Default.MemoryManager.Allocate<Rgba32>(length))
{
PixelOperations<TPixel>.Instance.ToRgba32(source, rgbaBuffer, length);
Span<Rgba32> rgbaSpan = rgbaBuffer.Span;
PixelOperations<TPixel>.Instance.ToRgba32(source, rgbaSpan, length);
for (int i = 0; i < length; i++)
{
ref Rgba32 s = ref rgbaBuffer[i];
ref Rgba32 s = ref rgbaSpan[i];
ref Argb32 d = ref dest[i];
d.PackFromRgba32(s);
@ -39,13 +40,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
int length = source.Length;
Guard.MustBeSizedAtLeast(dest, length, nameof(dest));
using (var rgbaBuffer = Configuration.Default.MemoryManager.Allocate<Rgba32>(length))
using (Buffer<Rgba32> rgbaBuffer = Configuration.Default.MemoryManager.Allocate<Rgba32>(length))
{
PixelOperations<Argb32>.Instance.ToRgba32(source, rgbaBuffer, length);
Span<Rgba32> rgbaSpan = rgbaBuffer.Span;
PixelOperations<Argb32>.Instance.ToRgba32(source, rgbaSpan, length);
for (int i = 0; i < length; i++)
{
ref Rgba32 s = ref rgbaBuffer[i];
ref Rgba32 s = ref rgbaSpan[i];
ref TPixel d = ref dest[i];
d.PackFromRgba32(s);
@ -59,13 +61,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
int length = source.Length;
Guard.MustBeSizedAtLeast(dest, length, nameof(dest));
using (var rgbaBuffer = Configuration.Default.MemoryManager.Allocate<Rgb24>(length))
using (Buffer<Rgb24> rgbBuffer = Configuration.Default.MemoryManager.Allocate<Rgb24>(length))
{
PixelOperations<Rgb24>.Instance.ToRgb24(source, rgbaBuffer, length);
Span<Rgb24> rgbSpan = rgbBuffer.Span;
PixelOperations<Rgb24>.Instance.ToRgb24(source, rgbSpan, length);
for (int i = 0; i < length; i++)
{
ref Rgb24 s = ref rgbaBuffer[i];
ref Rgb24 s = ref rgbSpan[i];
ref TPixel d = ref dest[i];
var rgba = default(Rgba32);
s.ToRgba32(ref rgba);

Loading…
Cancel
Save