Browse Source

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

af/merge-core
Anton Firszov 9 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) internal override void Apply(Span<float> scanline, int x, int y)
{ {
// Create a span for colors // Create a span for colors
using (var amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length)) using (Buffer<float> amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (var overlay = this.Target.MemoryManager.Allocate<TPixel>(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 sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX; int offsetX = x - this.offsetX;
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(sourceY); Span<TPixel> sourceRow = this.source.GetPixelRowSpan(sourceY);
for (int i = 0; i < scanline.Length; i++) 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; int sourceX = (i + offsetX) % this.xLength;
TPixel pixel = sourceRow[sourceX]; TPixel pixel = sourceRow[sourceX];
overlay[i] = pixel; overlaySpan[i] = pixel;
} }
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); 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) internal override void Apply(Span<float> scanline, int x, int y)
{ {
int patternY = y % this.pattern.Height; int patternY = y % this.pattern.Height;
using (var amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length)) using (Buffer<float> amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (var overlay = this.Target.MemoryManager.Allocate<TPixel>(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++) 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; 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); 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> /// <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) internal virtual void Apply(Span<float> scanline, int x, int y)
{ {
using (var amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length)) using (Buffer<float> amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (var overlay = this.Target.MemoryManager.Allocate<TPixel>(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++) for (int i = 0; i < scanline.Length; i++)
{ {
if (this.Options.BlendPercentage < 1) 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); 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 /> /// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y) internal override void Apply(Span<float> scanline, int x, int y)
{ {
using (var amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length)) using (Buffer<float> amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (var overlay = this.Target.MemoryManager.Allocate<TPixel>(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++) 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; int offsetX = x + i;
// no doubt this one can be optermised further but I can't imagine its // 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 // 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); 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) : base(source, options)
{ {
this.Colors = source.MemoryManager.Allocate<TPixel>(source.Width); this.Colors = source.MemoryManager.Allocate<TPixel>(source.Width);
for (int i = 0; i < this.Colors.Length; i++) this.Colors.Span.Fill(color);
{
this.Colors[i] = color;
}
} }
/// <summary> /// <summary>
@ -81,7 +78,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <returns> /// <returns>
/// The color /// The color
/// </returns> /// </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 /> /// <inheritdoc />
public override void Dispose() public override void Dispose()
@ -96,14 +93,16 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
{ {
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); 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++) 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) 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); maxY = Math.Min(this.Location.Y + this.Size.Height, maxY);
int width = maxX - minX; 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.Span.Fill(this.Alpha);
{
amount[i] = this.Alpha;
}
Parallel.For( Parallel.For(
minY, minY,
@ -99,7 +96,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors
{ {
Span<TPixel> background = source.GetPixelRowSpan(y).Slice(minX, width); Span<TPixel> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixel> foreground = targetImage.GetPixelRowSpan(y - this.Location.Y).Slice(targetX, 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; int width = maxX - minX;
using (var amount = source.MemoryManager.Allocate<float>(width)) using (Buffer<float> amount = source.MemoryManager.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options)) using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source,
sourceRectangle,
this.options))
{ {
for (int i = 0; i < width; i++) amount.Span.Fill(this.options.BlendPercentage);
{
amount[i] = this.options.BlendPercentage;
}
Parallel.For( Parallel.For(
minY, minY,
maxY, maxY,
configuration.ParallelOptions, configuration.ParallelOptions,
y => y =>
{ {
int offsetY = y - startY; int offsetY = y - startY;
int offsetX = minX - startX; 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)) using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
{ {
int scanlineWidth = maxX - minX; int scanlineWidth = maxX - minX;
using (FakeBuffer<float> buffer = source.MemoryManager.AllocateFake<float>(maxIntersections)) using (BasicArrayBuffer<float> buffer = source.MemoryManager.AllocateFake<float>(maxIntersections))
using (FakeBuffer<float> scanline = source.MemoryManager.AllocateFake<float>(scanlineWidth)) using (BasicArrayBuffer<float> scanline = source.MemoryManager.AllocateFake<float>(scanlineWidth))
{ {
bool scanlineDirty = true; bool scanlineDirty = true;
for (int y = minY; y < maxY; y++) 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 data = 0;
int first = 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++) for (code = 0; code < clearCode; code++)
{ {
this.prefix[code] = 0; prefixSpan[code] = 0;
this.suffix[code] = (byte)code; suffixSpan[code] = (byte)code;
} }
byte[] buffer = new byte[255]; byte[] buffer = new byte[255];
@ -172,7 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (oldCode == NullCode) if (oldCode == NullCode)
{ {
this.pixelStack[top++] = this.suffix[code]; pixelStackSpan[top++] = suffixSpan[code];
oldCode = code; oldCode = code;
first = code; first = code;
continue; continue;
@ -181,27 +185,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
int inCode = code; int inCode = code;
if (code == availableCode) if (code == availableCode)
{ {
this.pixelStack[top++] = (byte)first; pixelStackSpan[top++] = (byte)first;
code = oldCode; code = oldCode;
} }
while (code > clearCode) while (code > clearCode)
{ {
this.pixelStack[top++] = this.suffix[code]; pixelStackSpan[top++] = suffixSpan[code];
code = this.prefix[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 : // Fix for Gifs that have "deferred clear code" as per here :
// https://bugzilla.mozilla.org/show_bug.cgi?id=55918 // https://bugzilla.mozilla.org/show_bug.cgi?id=55918
if (availableCode < MaxStackSize) if (availableCode < MaxStackSize)
{ {
this.prefix[availableCode] = oldCode; prefixSpan[availableCode] = oldCode;
this.suffix[availableCode] = first; suffixSpan[availableCode] = first;
availableCode++; availableCode++;
if (availableCode == codeMask + 1 && availableCode < MaxStackSize) if (availableCode == codeMask + 1 && availableCode < MaxStackSize)
{ {
@ -217,7 +221,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
top--; top--;
// Clear missing pixels // 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> /// <param name="stream">The output stream.</param>
private void ClearBlock(Stream stream) private void ClearBlock(Stream stream)
{ {
this.ResetCodeTable(this.hsize); this.ResetCodeTable();
this.freeEntry = this.clearCode + 2; this.freeEntry = this.clearCode + 2;
this.clearFlag = true; this.clearFlag = true;
@ -269,13 +269,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Reset the code table. /// Reset the code table.
/// </summary> /// </summary>
/// <param name="size">The hash size.</param> private void ResetCodeTable()
private void ResetCodeTable(int size)
{ {
for (int i = 0; i < size; ++i) this.hashTable.Span.Fill(-1);
{
this.hashTable[i] = -1; // Original code:
} // for (int i = 0; i < size; ++i)
// {
// this.hashTable[i] = -1;
// }
} }
/// <summary> /// <summary>
@ -317,23 +319,26 @@ namespace SixLabors.ImageSharp.Formats.Gif
hsizeReg = this.hsize; hsizeReg = this.hsize;
this.ResetCodeTable(hsizeReg); // clear hash table this.ResetCodeTable(); // clear hash table
this.Output(this.clearCode, stream); this.Output(this.clearCode, stream);
Span<int> hashTableSpan = this.hashTable.Span;
Span<int> codeTableSpan = this.codeTable.Span;
while ((c = this.NextPixel()) != Eof) while ((c = this.NextPixel()) != Eof)
{ {
fcode = (c << this.maxbits) + ent; fcode = (c << this.maxbits) + ent;
int i = (c << hshift) ^ ent /* = 0 */; int i = (c << hshift) ^ ent /* = 0 */;
if (this.hashTable[i] == fcode) if (hashTableSpan[i] == fcode)
{ {
ent = this.codeTable[i]; ent = codeTableSpan[i];
continue; continue;
} }
// Non-empty slot // Non-empty slot
if (this.hashTable[i] >= 0) if (hashTableSpan[i] >= 0)
{ {
int disp = hsizeReg - i; int disp = hsizeReg - i;
if (i == 0) if (i == 0)
@ -348,15 +353,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
i += hsizeReg; i += hsizeReg;
} }
if (this.hashTable[i] == fcode) if (hashTableSpan[i] == fcode)
{ {
ent = this.codeTable[i]; ent = codeTableSpan[i];
break; break;
} }
} }
while (this.hashTable[i] >= 0); while (hashTableSpan[i] >= 0);
if (this.hashTable[i] == fcode) if (hashTableSpan[i] == fcode)
{ {
continue; continue;
} }
@ -366,8 +371,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
ent = c; ent = c;
if (this.freeEntry < this.maxmaxcode) if (this.freeEntry < this.maxmaxcode)
{ {
this.codeTable[i] = this.freeEntry++; // code -> hashtable codeTableSpan[i] = this.freeEntry++; // code -> hashtable
this.hashTable[i] = fcode; hashTableSpan[i] = fcode;
} }
else 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; int y = yy - this.PixelRowCounter;
var values = new ColorConverters.JpegColorConverter.ComponentValues(buffers, y); 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); 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 /// 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. /// has to be able to unread more than 1 byte, due to byte stuffing.
/// Byte stuffing is specified in section F.1.2.3. /// Byte stuffing is specified in section F.1.2.3.
/// TODO: Optimize buffer management inside this class!
/// </summary> /// </summary>
internal struct Bytes : IDisposable internal struct Bytes : IDisposable
{ {
@ -26,7 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Gets or sets the buffer. /// Gets or sets the buffer.
/// buffer[i:j] are the buffered bytes read from the underlying /// buffer[i:j] are the buffered bytes read from the underlying
/// stream that haven't yet been passed further on. /// stream that haven't yet been passed further on.
/// TODO: Do we really need buffer here? Might be an optimiziation opportunity.
/// </summary> /// </summary>
public IManagedByteBuffer Buffer; 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. // Take the fast path if bytes.buf contains at least two bytes.
if (this.I + 2 <= this.J) if (this.I + 2 <= this.J)
{ {
x = this.BufferAsInt[this.I]; Span<int> bufferSpan = this.BufferAsInt.Span;
x = bufferSpan[this.I];
this.I++; this.I++;
this.UnreadableBytes = 1; this.UnreadableBytes = 1;
if (x != OrigJpegConstants.Markers.XFFInt) if (x != OrigJpegConstants.Markers.XFFInt)
@ -96,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
return OrigDecoderErrorCode.NoError; return OrigDecoderErrorCode.NoError;
} }
if (this.BufferAsInt[this.I] != 0x00) if (bufferSpan[this.I] != 0x00)
{ {
return OrigDecoderErrorCode.MissingFF00; 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.I++;
this.UnreadableBytes = 0; this.UnreadableBytes = 0;
return errorCode; return errorCode;
@ -230,20 +231,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
DecoderThrowHelper.ThrowImageFormatException.FillCalledWhenUnreadBytesExist(); 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 // Move the last 2 bytes to the start of the buffer, in case we need
// to call UnreadByteStuffedByte. // to call UnreadByteStuffedByte.
if (this.J > 2) if (this.J > 2)
{ {
bufferSpan[0] = bufferSpan[this.J - 2]; byteSpan[0] = byteSpan[this.J - 2];
bufferSpan[1] = bufferSpan[this.J - 1]; byteSpan[1] = byteSpan[this.J - 1];
this.I = 2; this.I = 2;
this.J = 2; this.J = 2;
} }
// Fill in the rest of the buffer. // 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) if (n == 0)
{ {
return OrigDecoderErrorCode.UnexpectedEndOfStream; return OrigDecoderErrorCode.UnexpectedEndOfStream;
@ -251,9 +252,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.J += n; 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; return OrigDecoderErrorCode.NoError;

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

@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary> /// </summary>
internal struct PdfJsHuffmanTable : IDisposable internal struct PdfJsHuffmanTable : IDisposable
{ {
private FakeBuffer<short> lookahead; private BasicArrayBuffer<short> lookahead;
private FakeBuffer<short> valOffset; private BasicArrayBuffer<short> valOffset;
private FakeBuffer<long> maxcode; private BasicArrayBuffer<long> maxcode;
private IManagedByteBuffer huffval; private IManagedByteBuffer huffval;
/// <summary> /// <summary>
@ -30,8 +30,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.valOffset = memoryManager.AllocateFake<short>(18); this.valOffset = memoryManager.AllocateFake<short>(18);
this.maxcode = memoryManager.AllocateFake<long>(18); this.maxcode = memoryManager.AllocateFake<long>(18);
using (FakeBuffer<short> huffsize = memoryManager.AllocateFake<short>(257)) using (BasicArrayBuffer<short> huffsize = memoryManager.AllocateFake<short>(257))
using (FakeBuffer<short> huffcode = memoryManager.AllocateFake<short>(257)) using (BasicArrayBuffer<short> huffcode = memoryManager.AllocateFake<short>(257))
{ {
GenerateSizeTable(lengths, huffsize); GenerateSizeTable(lengths, huffsize);
GenerateCodeTable(huffsize, huffcode); 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); var scale = new Vector2(this.imageWidth / (float)width, this.imageHeight / (float)height);
this.componentData = this.memoryManager.Allocate<byte>(width * height * numberOfComponents); 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 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++) for (int i = 0; i < numberOfComponents; i++)
{ {
ref PdfJsComponent component = ref components.Components[i]; ref PdfJsComponent component = ref components.Components[i];
Vector2 componentScale = component.Scale * scale; Vector2 componentScale = component.Scale * scale;
int offset = i; int offset = i;
Span<short> output = component.Output; Span<short> output = component.Output.Span;
int blocksPerScanline = (component.BlocksPerLine + 1) << 3; int blocksPerScanline = (component.BlocksPerLine + 1) << 3;
// Precalculate the xScaleBlockOffset // 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBaseline(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) 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); int t = this.DecodeHuffman(ref dcHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached) 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); 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; int k = 1;
while (k < 64) while (k < 64)
@ -748,7 +750,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
byte z = PdfJsQuantizationTables.DctZigZag[k]; byte z = PdfJsQuantizationTables.DctZigZag[k];
short re = (short)this.ReceiveAndExtend(s, stream); short re = (short)this.ReceiveAndExtend(s, stream);
component.BlockData[offset + z] = re; blockDataSpan[offset + z] = re;
k++; k++;
} }
} }
@ -756,6 +758,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeDCFirst(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, Stream stream) 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); int t = this.DecodeHuffman(ref dcHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached) 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; 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeDCSuccessive(PdfJsFrameComponent component, int offset, Stream stream) private void DecodeDCSuccessive(PdfJsFrameComponent component, int offset, Stream stream)
{ {
Span<short> blockDataSpan = component.BlockData.Span;
int bit = this.ReadBit(stream); int bit = this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached) if (this.endOfStreamReached || this.unexpectedMarkerReached)
{ {
return; return;
} }
component.BlockData[offset] |= (short)(bit << this.successiveState); blockDataSpan[offset] |= (short)(bit << this.successiveState);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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)) using (Buffer<short> multiplicationBuffer = this.configuration.MemoryManager.Allocate<short>(64, true))
{ {
Span<short> quantizationTable = this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex); 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 // For AA&N IDCT method, multiplier are equal to quantization
// coefficients scaled by scalefactor[row]*scalefactor[col], where // coefficients scaled by scalefactor[row]*scalefactor[col], where
// scalefactor[0] = 1 // scalefactor[0] = 1
// scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 // scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7
// For integer operation, the multiplier table is to be scaled by 12. // 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++) // 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 namespace SixLabors.ImageSharp.Memory
{ {
/// <summary> /// <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> /// </summary>
internal class FakeBuffer<T> : IBuffer<T> internal class BasicArrayBuffer<T> : IBuffer<T>
where T : struct where T : struct
{ {
public FakeBuffer(T[] array) public BasicArrayBuffer(T[] array)
{ {
this.Array = array; this.Array = array;
} }
@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Memory
/// </summary> /// </summary>
/// <param name="buffer">The <see cref="Buffer{T}"/> to convert.</param> /// <param name="buffer">The <see cref="Buffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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); return new ReadOnlySpan<T>(buffer.Array, 0, buffer.Length);
} }
@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Memory
/// </summary> /// </summary>
/// <param name="buffer">The <see cref="Buffer{T}"/> to convert.</param> /// <param name="buffer">The <see cref="Buffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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); 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; 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> /// <summary>
/// Gets the count of "relevant" elements. It's usually smaller than 'Array.Length' when <see cref="array"/> is pooled. /// Gets the count of "relevant" elements. It's usually smaller than 'Array.Length' when <see cref="array"/> is pooled.
/// </summary> /// </summary>
@ -51,44 +46,7 @@ namespace SixLabors.ImageSharp.Memory
/// <summary> /// <summary>
/// Gets a <see cref="Span{T}"/> to the backing buffer. /// Gets a <see cref="Span{T}"/> to the backing buffer.
/// </summary> /// </summary>
public Span<T> Span => this; public Span<T> Span => new Span<T>(this.array, 0, this.Length);
/// <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);
}
/// <summary> /// <summary>
/// Disposes the <see cref="Buffer{T}"/> instance by unpinning the array, and returning the pooled buffer when necessary. /// 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() public void Dispose()
{ {
if (this.IsDisposedOrLostArrayOwnership) if (this.array == null)
{ {
return; return;
} }
this.IsDisposedOrLostArrayOwnership = true;
this.memoryManager?.Release(this); this.memoryManager?.Release(this);
this.memoryManager = null; this.memoryManager = null;
@ -112,27 +68,6 @@ namespace SixLabors.ImageSharp.Memory
GC.SuppressFinalize(this); 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> /// <summary>
/// TODO: Refactor this /// TODO: Refactor this
/// </summary> /// </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. /// 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! /// Should be replaced with 'Allocate()' as soon as SixLabors.Shapes has Span-based API-s!
/// </summary> /// </summary>
internal FakeBuffer<T> AllocateFake<T>(int length, bool dummy = false) internal BasicArrayBuffer<T> AllocateFake<T>(int length, bool dummy = false)
where T : struct 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; int width = maxX - minX;
using (var colors = this.memoryManager.Allocate<TPixel>(width)) using (Buffer<TPixel> colors = this.memoryManager.Allocate<TPixel>(width))
using (var amount = this.memoryManager.Allocate<float>(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++) for (int i = 0; i < width; i++)
{ {
colors[i] = this.Value; colorSpan[i] = this.Value;
amount[i] = this.options.BlendPercentage; amountSpan[i] = this.options.BlendPercentage;
} }
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(this.options.BlenderMode); 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); 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 // 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; TPixel glowColor = this.GlowColor;
Vector2 centre = Rectangle.Center(sourceRectangle); 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; 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; 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++) for (int i = 0; i < width; i++)
{ {
rowColors[i] = glowColor; rowColorsSpan[i] = glowColor;
} }
Parallel.For( Parallel.For(
@ -100,19 +103,20 @@ namespace SixLabors.ImageSharp.Processing.Processors
configuration.ParallelOptions, configuration.ParallelOptions,
y => 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 offsetY = y - startY;
int offsetX = minX - startX; int offsetX = minX - startX;
for (int i = 0; i < width; i++) for (int i = 0; i < width; i++)
{ {
float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); 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); 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; TPixel vignetteColor = this.VignetteColor;
Vector2 centre = Rectangle.Center(sourceRectangle); Vector2 centre = Rectangle.Center(sourceRectangle);
var finalradiusX = this.RadiusX.Calculate(source.Size()); float finalradiusX = this.RadiusX.Calculate(source.Size());
var finalradiusY = this.RadiusY.Calculate(source.Size()); float finalradiusY = this.RadiusY.Calculate(source.Size());
float rX = finalradiusX > 0 ? MathF.Min(finalradiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; 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 rY = finalradiusY > 0 ? MathF.Min(finalradiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F;
float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY)); float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY));
@ -110,11 +110,14 @@ namespace SixLabors.ImageSharp.Processing.Processors
} }
int width = maxX - minX; 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++) for (int i = 0; i < width; i++)
{ {
rowColors[i] = vignetteColor; rowColorsSpan[i] = vignetteColor;
} }
Parallel.For( Parallel.For(
@ -123,19 +126,20 @@ namespace SixLabors.ImageSharp.Processing.Processors
configuration.ParallelOptions, configuration.ParallelOptions,
y => 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 offsetY = y - startY;
int offsetX = minX - startX; int offsetX = minX - startX;
for (int i = 0; i < width; i++) for (int i = 0; i < width; i++)
{ {
float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); 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); 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 => y =>
{ {
// TODO: Without Parallel.For() this buffer object could be reused: // 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<Vector4> firstPassRow = firstPassPixels.GetRowSpan(y);
Span<TPixel> sourceRow = source.GetPixelRowSpan(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) if (this.Compand)
{ {
for (int x = minX; x < maxX; x++) for (int x = minX; x < maxX; x++)
{ {
WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; WeightsWindow window = this.HorizontalWeights.Weights[x - startX];
firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX); firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer.Span, sourceX);
} }
} }
else else
@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
for (int x = minX; x < maxX; x++) for (int x = minX; x < maxX; x++)
{ {
WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; 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> /// <summary>
/// Override this to process the pixel in the first pass of the algorithm /// 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> /// </summary>
/// <param name="pixel">The pixel to quantize</param> /// <param name="pixel">The pixel to quantize</param>
/// <remarks> /// <remarks>

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

@ -36,6 +36,14 @@ namespace SixLabors.ImageSharp.Quantizers
public class WuQuantizer<TPixel> : QuantizerBase<TPixel> public class WuQuantizer<TPixel> : QuantizerBase<TPixel>
where TPixel : struct, IPixel<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> /// <summary>
/// The index bits. /// The index bits.
/// </summary> /// </summary>
@ -69,37 +77,37 @@ namespace SixLabors.ImageSharp.Quantizers
/// <summary> /// <summary>
/// Moment of <c>P(c)</c>. /// Moment of <c>P(c)</c>.
/// </summary> /// </summary>
private Buffer<long> vwt; private IBuffer<long> vwt;
/// <summary> /// <summary>
/// Moment of <c>r*P(c)</c>. /// Moment of <c>r*P(c)</c>.
/// </summary> /// </summary>
private Buffer<long> vmr; private IBuffer<long> vmr;
/// <summary> /// <summary>
/// Moment of <c>g*P(c)</c>. /// Moment of <c>g*P(c)</c>.
/// </summary> /// </summary>
private Buffer<long> vmg; private IBuffer<long> vmg;
/// <summary> /// <summary>
/// Moment of <c>b*P(c)</c>. /// Moment of <c>b*P(c)</c>.
/// </summary> /// </summary>
private Buffer<long> vmb; private IBuffer<long> vmb;
/// <summary> /// <summary>
/// Moment of <c>a*P(c)</c>. /// Moment of <c>a*P(c)</c>.
/// </summary> /// </summary>
private Buffer<long> vma; private IBuffer<long> vma;
/// <summary> /// <summary>
/// Moment of <c>c^2*P(c)</c>. /// Moment of <c>c^2*P(c)</c>.
/// </summary> /// </summary>
private Buffer<float> m2; private IBuffer<float> m2;
/// <summary> /// <summary>
/// Color space tag. /// Color space tag.
/// </summary> /// </summary>
private Buffer<byte> tag; private IBuffer<byte> tag;
/// <summary> /// <summary>
/// Maximum allowed color depth /// Maximum allowed color depth
@ -153,23 +161,16 @@ namespace SixLabors.ImageSharp.Quantizers
} }
finally finally
{ {
this.DisposeBuffer(ref this.vwt); this.vwt.Dispose();
this.DisposeBuffer(ref this.vmr); this.vmr.Dispose();
this.DisposeBuffer(ref this.vmg); this.vmg.Dispose();
this.DisposeBuffer(ref this.vmb); this.vmb.Dispose();
this.DisposeBuffer(ref this.vma); this.vma.Dispose();
this.DisposeBuffer(ref this.m2); this.m2.Dispose();
this.DisposeBuffer(ref this.tag); this.tag.Dispose();
} }
} }
private void DisposeBuffer<T>(ref Buffer<T> buffer)
where T : struct
{
buffer?.Dispose();
buffer = null;
}
/// <inheritdoc/> /// <inheritdoc/>
protected override TPixel[] GetPalette() protected override TPixel[] GetPalette()
{ {
@ -213,14 +214,21 @@ namespace SixLabors.ImageSharp.Quantizers
int index = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); int index = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1);
this.vwt[index]++; Span<long> vwtSpan = this.vwt.Span;
this.vmr[index] += rgba.R; Span<long> vmrSpan = this.vmr.Span;
this.vmg[index] += rgba.G; Span<long> vmgSpan = this.vmg.Span;
this.vmb[index] += rgba.B; Span<long> vmbSpan = this.vmb.Span;
this.vma[index] += rgba.A; 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); 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/> /// <inheritdoc/>
@ -458,6 +466,13 @@ namespace SixLabors.ImageSharp.Quantizers
/// </summary> /// </summary>
private void Get3DMoments(MemoryManager memoryManager) 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> volume = memoryManager.Allocate<long>(IndexCount * IndexAlphaCount))
using (Buffer<long> volumeR = memoryManager.Allocate<long>(IndexCount * IndexAlphaCount)) using (Buffer<long> volumeR = memoryManager.Allocate<long>(IndexCount * IndexAlphaCount))
using (Buffer<long> volumeG = 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<long> areaA = memoryManager.Allocate<long>(IndexAlphaCount))
using (Buffer<float> area2 = memoryManager.Allocate<float>(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++) for (int r = 1; r < IndexCount; r++)
{ {
volume.Clear(); volume.Clear();
@ -503,37 +532,37 @@ namespace SixLabors.ImageSharp.Quantizers
{ {
int ind1 = GetPaletteIndex(r, g, b, a); int ind1 = GetPaletteIndex(r, g, b, a);
line += this.vwt[ind1]; line += vwtSpan[ind1];
lineR += this.vmr[ind1]; lineR += vmrSpan[ind1];
lineG += this.vmg[ind1]; lineG += vmgSpan[ind1];
lineB += this.vmb[ind1]; lineB += vmbSpan[ind1];
lineA += this.vma[ind1]; lineA += vmaSpan[ind1];
line2 += this.m2[ind1]; line2 += m2Span[ind1];
area[a] += line; areaSpan[a] += line;
areaR[a] += lineR; areaRSpan[a] += lineR;
areaG[a] += lineG; areaGSpan[a] += lineG;
areaB[a] += lineB; areaBSpan[a] += lineB;
areaA[a] += lineA; areaASpan[a] += lineA;
area2[a] += line2; area2Span[a] += line2;
int inv = (b * IndexAlphaCount) + a; int inv = (b * IndexAlphaCount) + a;
volume[inv] += area[a]; volumeSpan[inv] += areaSpan[a];
volumeR[inv] += areaR[a]; volumeRSpan[inv] += areaRSpan[a];
volumeG[inv] += areaG[a]; volumeGSpan[inv] += areaGSpan[a];
volumeB[inv] += areaB[a]; volumeBSpan[inv] += areaBSpan[a];
volumeA[inv] += areaA[a]; volumeASpan[inv] += areaASpan[a];
volume2[inv] += area2[a]; volume2Span[inv] += area2Span[a];
int ind2 = ind1 - GetPaletteIndex(1, 0, 0, 0); int ind2 = ind1 - GetPaletteIndex(1, 0, 0, 0);
this.vwt[ind1] = this.vwt[ind2] + volume[inv]; vwtSpan[ind1] = vwtSpan[ind2] + volumeSpan[inv];
this.vmr[ind1] = this.vmr[ind2] + volumeR[inv]; vmrSpan[ind1] = vmrSpan[ind2] + volumeRSpan[inv];
this.vmg[ind1] = this.vmg[ind2] + volumeG[inv]; vmgSpan[ind1] = vmgSpan[ind2] + volumeGSpan[inv];
this.vmb[ind1] = this.vmb[ind2] + volumeB[inv]; vmbSpan[ind1] = vmbSpan[ind2] + volumeBSpan[inv];
this.vma[ind1] = this.vma[ind2] + volumeA[inv]; vmaSpan[ind1] = vmaSpan[ind2] + volumeASpan[inv];
this.m2[ind1] = this.m2[ind2] + volume2[inv]; m2Span[ind1] = m2Span[ind2] + volume2Span[inv];
} }
} }
} }
@ -553,23 +582,25 @@ namespace SixLabors.ImageSharp.Quantizers
float db = Volume(ref cube, this.vmb.Span); float db = Volume(ref cube, this.vmb.Span);
float da = Volume(ref cube, this.vma.Span); float da = Volume(ref cube, this.vma.Span);
Span<float> m2Span = this.m2.Span;
float xx = float xx =
this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)]
- this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] - m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)]
- this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] - m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)]
+ this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] + m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
- this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)] - m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)]
+ this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] + m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
+ this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] + m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
- this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
- this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)] - m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)]
+ this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] + m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
+ this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] + m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
- this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
+ this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)] + m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)]
- this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
- this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
+ this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
var vector = new Vector4(dr, dg, db, da); var vector = new Vector4(dr, dg, db, da);
return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.Span)); 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> /// <param name="label">A label.</param>
private void Mark(ref Box cube, byte label) 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 r = cube.R0 + 1; r <= cube.R1; r++)
{ {
for (int g = cube.G0 + 1; g <= cube.G1; g++) 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++) 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 b = rgba.B >> (8 - IndexBits);
int a = rgba.A >> (8 - IndexAlphaBits); 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] [Benchmark]
public void CommonBulk() 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] [Benchmark]
public void OptimizedBulk() 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] [Benchmark]
public void CommonBulk() 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] [Benchmark]
public void OptimizedBulk() 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] [Benchmark]
public void CommonBulk() 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] [Benchmark]
public void OptimizedBulk() 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] [Benchmark]
public void CommonBulk() 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] [Benchmark]
public void OptimizedBulk() 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] [Benchmark]
public void CommonBulk() 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] [Benchmark]
public void OptimizedBulk() 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)) using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
{ {
Buffer<float> amounts = Configuration.Default.MemoryManager.Allocate<float>(image.Width); using (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())
{ {
for (int y = 0; y < image.Height; y++) amounts.Span.Fill(1);
using (PixelAccessor<Rgba32> pixels = image.Lock())
{ {
Span<Rgba32> span = pixels.GetRowSpan(y); for (int y = 0; y < image.Height; y++)
BulkVectorConvert(span, span, span, amounts); {
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)) using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
{ {
Buffer<float> amounts = Configuration.Default.MemoryManager.Allocate<float>(image.Width); using (Buffer<float> amounts = Configuration.Default.MemoryManager.Allocate<float>(image.Width))
for (int x = 0; x < image.Width; x++)
{ {
amounts[x] = 1; amounts.Span.Fill(1);
} using (PixelAccessor<Rgba32> pixels = image.Lock())
using (PixelAccessor<Rgba32> pixels = image.Lock())
{
for (int y = 0; y < image.Height; y++)
{ {
Span<Rgba32> span = pixels.GetRowSpan(y); for (int y = 0; y < image.Height; y++)
BulkPixelConvert(span, span, span, amounts); {
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 (Buffer<TPixel> rowColors = Configuration.Default.MemoryManager.Allocate<TPixel>(width))
using (PixelAccessor<TPixel> sourcePixels = source.Lock()) using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{ {
for (int i = 0; i < width; i++) rowColors.Span.Fill(glowColor);
{
rowColors[i] = glowColor;
}
Parallel.For( Parallel.For(
minY, 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 // 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); return new JpegColorConverter.ComponentValues(buffers, 0);
} }

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

@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
TestOperation( TestOperation(
source, source,
expected, 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, 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( TestOperation(
source, source,
expected, 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( TestOperation(
source, source,
expected, 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( TestOperation(
source, source,
expected, 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( TestOperation(
source, source,
expected, 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( TestOperation(
source, source,
expected, 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( TestOperation(
source, source,
expected, 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( TestOperation(
source, source,
expected, 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( TestOperation(
source, source,
expected, 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( TestOperation(
source, source,
expected, 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( TestOperation(
source, source,
expected, 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 TSource[] SourceBuffer { get; }
public Buffer<TDest> ActualDestBuffer { get; } public Buffer<TDest> ActualDestBuffer { get; }
public TDest[] ExpectedDestBuffer { get; } public TDest[] ExpectedDestBuffer { get; }
public Span<TSource> Source => this.SourceBuffer;
public Span<TDest> ActualDest => this.ActualDestBuffer;
public TestBuffers(TSource[] source, TDest[] expectedDest) public TestBuffers(TSource[] source, TDest[] expectedDest)
{ {
this.SourceBuffer = source; this.SourceBuffer = source;
@ -386,7 +383,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
internal static void TestOperation<TSource, TDest>( internal static void TestOperation<TSource, TDest>(
TSource[] source, TSource[] source,
TDest[] expected, TDest[] expected,
Action<TSource[], Buffer<TDest>> action) Action<TSource[], IBuffer<TDest>> action)
where TSource : struct where TSource : struct
where TDest : 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; int length = source.Length;
Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); 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++) 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]; ref Argb32 d = ref dest[i];
d.PackFromRgba32(s); d.PackFromRgba32(s);
@ -39,13 +40,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
int length = source.Length; int length = source.Length;
Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); 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++) 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]; ref TPixel d = ref dest[i];
d.PackFromRgba32(s); d.PackFromRgba32(s);
@ -59,13 +61,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
int length = source.Length; int length = source.Length;
Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); 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++) 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]; ref TPixel d = ref dest[i];
var rgba = default(Rgba32); var rgba = default(Rgba32);
s.ToRgba32(ref rgba); s.ToRgba32(ref rgba);

Loading…
Cancel
Save