Browse Source

remove ImageFrame.GetPixelRowSpan

af/UniformUnmanagedMemoryPoolMemoryAllocator-02-MemoryGuards
Anton Firszov 5 years ago
parent
commit
9047fc971d
  1. 14
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  2. 2
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  3. 5
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  4. 12
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  5. 4
      src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs
  6. 4
      src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs
  7. 2
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  8. 6
      src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs
  9. 32
      src/ImageSharp/ImageFrame{TPixel}.cs
  10. 2
      src/ImageSharp/Image{TPixel}.cs
  11. 2
      src/ImageSharp/IndexedImageFrame{TPixel}.cs
  12. 2
      src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.CommonFactoryMethods.cs
  13. 2
      src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs
  14. 2
      src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.Buffer{T}.cs
  15. 14
      src/ImageSharp/Memory/Buffer2DExtensions.cs
  16. 4
      src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs
  17. 12
      src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs
  18. 9
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
  19. 12
      src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs
  20. 7
      src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs
  21. 18
      src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
  22. 10
      src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
  23. 8
      src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
  24. 9
      src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs
  25. 8
      src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
  26. 9
      src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs
  27. 44
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
  28. 14
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs
  29. 16
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
  30. 8
      src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs
  31. 8
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
  32. 8
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs
  33. 5
      src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs
  34. 3
      src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs
  35. 12
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs
  36. 39
      src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
  37. 17
      src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs
  38. 39
      src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
  39. 38
      src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs
  40. 16
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
  41. 4
      src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs
  42. 4
      tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
  43. 4
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  44. 2
      tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs
  45. 16
      tests/ImageSharp.Tests/Image/ImageCloneTests.cs
  46. 12
      tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
  47. 2
      tests/ImageSharp.Tests/Image/ImageTests.cs
  48. 4
      tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs
  49. 16
      tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs
  50. 7
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs
  51. 7
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs
  52. 4
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs
  53. 20
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs
  54. 8
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

14
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -379,7 +379,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int y = image.Height - 1; y >= 0; y--) for (int y = image.Height - 1; y >= 0; y--)
{ {
ReadOnlySpan<byte> pixelSpan = quantized.GetPixelRowSpan(y); ReadOnlySpan<byte> pixelSpan = quantized.DangerousGetRowSpan(y);
stream.Write(pixelSpan); stream.Write(pixelSpan);
for (int i = 0; i < this.padding; i++) for (int i = 0; i < this.padding; i++)
@ -413,10 +413,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp
} }
stream.Write(colorPalette); stream.Write(colorPalette);
Buffer2D<TPixel> imageBuffer = image.PixelBuffer;
for (int y = image.Height - 1; y >= 0; y--) for (int y = image.Height - 1; y >= 0; y--)
{ {
ReadOnlySpan<TPixel> inputPixelRow = image.GetPixelRowSpan(y); ReadOnlySpan<TPixel> inputPixelRow = imageBuffer.DangerousGetRowSpan(y);
ReadOnlySpan<byte> outputPixelRow = MemoryMarshal.AsBytes(inputPixelRow); ReadOnlySpan<byte> outputPixelRow = MemoryMarshal.AsBytes(inputPixelRow);
stream.Write(outputPixelRow); stream.Write(outputPixelRow);
@ -447,11 +447,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span; ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);
ReadOnlySpan<byte> pixelRowSpan = quantized.GetPixelRowSpan(0); ReadOnlySpan<byte> pixelRowSpan = quantized.DangerousGetRowSpan(0);
int rowPadding = pixelRowSpan.Length % 2 != 0 ? this.padding - 1 : this.padding; int rowPadding = pixelRowSpan.Length % 2 != 0 ? this.padding - 1 : this.padding;
for (int y = image.Height - 1; y >= 0; y--) for (int y = image.Height - 1; y >= 0; y--)
{ {
pixelRowSpan = quantized.GetPixelRowSpan(y); pixelRowSpan = quantized.DangerousGetRowSpan(y);
int endIdx = pixelRowSpan.Length % 2 == 0 ? pixelRowSpan.Length : pixelRowSpan.Length - 1; int endIdx = pixelRowSpan.Length % 2 == 0 ? pixelRowSpan.Length : pixelRowSpan.Length - 1;
for (int i = 0; i < endIdx; i += 2) for (int i = 0; i < endIdx; i += 2)
@ -491,11 +491,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span; ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);
ReadOnlySpan<byte> quantizedPixelRow = quantized.GetPixelRowSpan(0); ReadOnlySpan<byte> quantizedPixelRow = quantized.DangerousGetRowSpan(0);
int rowPadding = quantizedPixelRow.Length % 8 != 0 ? this.padding - 1 : this.padding; int rowPadding = quantizedPixelRow.Length % 8 != 0 ? this.padding - 1 : this.padding;
for (int y = image.Height - 1; y >= 0; y--) for (int y = image.Height - 1; y >= 0; y--)
{ {
quantizedPixelRow = quantized.GetPixelRowSpan(y); quantizedPixelRow = quantized.DangerousGetRowSpan(y);
int endIdx = quantizedPixelRow.Length % 8 == 0 ? quantizedPixelRow.Length : quantizedPixelRow.Length - 8; int endIdx = quantizedPixelRow.Length % 8 == 0 ? quantizedPixelRow.Length : quantizedPixelRow.Length - 8;
for (int i = 0; i < endIdx; i += 8) for (int i = 0; i < endIdx; i += 8)

2
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -481,7 +481,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
writeY = y; writeY = y;
} }
ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.GetPixelRowSpan(writeY)); ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.PixelBuffer.DangerousGetRowSpan(writeY));
if (!transFlag) if (!transFlag)
{ {

5
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -565,6 +565,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
int pass = 0; int pass = 0;
int width = this.header.Width; int width = this.header.Width;
Buffer2D<TPixel> imageBuffer = image.PixelBuffer;
while (true) while (true)
{ {
int numColumns = Adam7.ComputeColumns(width, pass); int numColumns = Adam7.ComputeColumns(width, pass);
@ -623,7 +624,7 @@ namespace SixLabors.ImageSharp.Formats.Png
break; break;
} }
Span<TPixel> rowSpan = image.GetPixelRowSpan(this.currentRow); Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(this.currentRow);
this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetadata, Adam7.FirstColumn[pass], Adam7.ColumnIncrement[pass]); this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetadata, Adam7.FirstColumn[pass], Adam7.ColumnIncrement[pass]);
this.SwapScanlineBuffers(); this.SwapScanlineBuffers();
@ -656,7 +657,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels, PngMetadata pngMetadata) private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels, PngMetadata pngMetadata)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Span<TPixel> rowSpan = pixels.GetPixelRowSpan(this.currentRow); Span<TPixel> rowSpan = pixels.PixelBuffer.DangerousGetRowSpan(this.currentRow);
// Trim the first marker byte from the buffer // Trim the first marker byte from the buffer
ReadOnlySpan<byte> trimmed = defilteredScanline.Slice(1, defilteredScanline.Length - 1); ReadOnlySpan<byte> trimmed = defilteredScanline.Slice(1, defilteredScanline.Length - 1);

12
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -168,7 +168,7 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba32 rgba32 = default; Rgba32 rgba32 = default;
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
Span<TPixel> span = image.GetPixelRowSpan(y); Span<TPixel> span = image.DangerousGetRowSpan(y);
for (int x = 0; x < image.Width; x++) for (int x = 0; x < image.Width; x++)
{ {
span[x].ToRgba32(ref rgba32); span[x].ToRgba32(ref rgba32);
@ -391,11 +391,11 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.bitDepth < 8) if (this.bitDepth < 8)
{ {
PngEncoderHelpers.ScaleDownFrom8BitArray(quantized.GetPixelRowSpan(row), this.currentScanline.GetSpan(), this.bitDepth); PngEncoderHelpers.ScaleDownFrom8BitArray(quantized.DangerousGetRowSpan(row), this.currentScanline.GetSpan(), this.bitDepth);
} }
else else
{ {
quantized.GetPixelRowSpan(row).CopyTo(this.currentScanline.GetSpan()); quantized.DangerousGetRowSpan(row).CopyTo(this.currentScanline.GetSpan());
} }
break; break;
@ -918,7 +918,7 @@ namespace SixLabors.ImageSharp.Formats.Png
Span<byte> attempt = attemptBuffer.GetSpan(); Span<byte> attempt = attemptBuffer.GetSpan();
for (int y = 0; y < this.height; y++) for (int y = 0; y < this.height; y++)
{ {
this.CollectAndFilterPixelRow(pixels.GetPixelRowSpan(y), ref filter, ref attempt, quantized, y); this.CollectAndFilterPixelRow(pixels.DangerousGetRowSpan(y), ref filter, ref attempt, quantized, y);
deflateStream.Write(filter); deflateStream.Write(filter);
this.SwapScanlineBuffers(); this.SwapScanlineBuffers();
} }
@ -959,7 +959,7 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int row = startRow; row < height; row += Adam7.RowIncrement[pass]) for (int row = startRow; row < height; row += Adam7.RowIncrement[pass])
{ {
// Collect pixel data // Collect pixel data
Span<TPixel> srcRow = pixels.GetPixelRowSpan(row); Span<TPixel> srcRow = pixels.DangerousGetRowSpan(row);
for (int col = startCol, i = 0; col < width; col += Adam7.ColumnIncrement[pass]) for (int col = startCol, i = 0; col < width; col += Adam7.ColumnIncrement[pass])
{ {
block[i++] = srcRow[col]; block[i++] = srcRow[col];
@ -1014,7 +1014,7 @@ namespace SixLabors.ImageSharp.Formats.Png
row += Adam7.RowIncrement[pass]) row += Adam7.RowIncrement[pass])
{ {
// Collect data // Collect data
ReadOnlySpan<byte> srcRow = quantized.GetPixelRowSpan(row); ReadOnlySpan<byte> srcRow = quantized.DangerousGetRowSpan(row);
for (int col = startCol, i = 0; for (int col = startCol, i = 0;
col < width; col < width;
col += Adam7.ColumnIncrement[pass]) col += Adam7.ColumnIncrement[pass])

4
src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs

@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
int grayRowIdx = 0; int grayRowIdx = 0;
for (int row = y; row < lastRow; row++) for (int row = y; row < lastRow; row++)
{ {
Span<TPixel> pixelsBlackWhiteRow = this.imageBlackWhite.GetPixelRowSpan(row); Span<TPixel> pixelsBlackWhiteRow = this.imageBlackWhite.DangerousGetRowSpan(row);
Span<byte> pixelAsGrayRow = pixelAsGraySpan.Slice(grayRowIdx * width, width); Span<byte> pixelAsGrayRow = pixelAsGraySpan.Slice(grayRowIdx * width, width);
PixelOperations<TPixel>.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGrayRow, width); PixelOperations<TPixel>.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGrayRow, width);
grayRowIdx++; grayRowIdx++;
@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
int bitIndex = 0; int bitIndex = 0;
int byteIndex = 0; int byteIndex = 0;
Span<byte> outputRow = rows.Slice(outputRowIdx * this.BytesPerRow); Span<byte> outputRow = rows.Slice(outputRowIdx * this.BytesPerRow);
Span<TPixel> pixelsBlackWhiteRow = this.imageBlackWhite.GetPixelRowSpan(row); Span<TPixel> pixelsBlackWhiteRow = this.imageBlackWhite.DangerousGetRowSpan(row);
PixelOperations<TPixel>.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGraySpan, width); PixelOperations<TPixel>.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGraySpan, width);
for (int x = 0; x < this.Image.Width; x++) for (int x = 0; x < this.Image.Width; x++)
{ {

4
src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs

@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
int lastRow = y + height; int lastRow = y + height;
for (int row = y; row < lastRow; row++) for (int row = y; row < lastRow; row++)
{ {
ReadOnlySpan<byte> indexedPixelRow = this.quantizedImage.GetPixelRowSpan(row); ReadOnlySpan<byte> indexedPixelRow = this.quantizedImage.DangerousGetRowSpan(row);
int idxPixels = 0; int idxPixels = 0;
for (int x = 0; x < halfWidth; x++) for (int x = 0; x < halfWidth; x++)
{ {
@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
int indexedPixelsRowIdx = 0; int indexedPixelsRowIdx = 0;
for (int row = y; row < lastRow; row++) for (int row = y; row < lastRow; row++)
{ {
ReadOnlySpan<byte> indexedPixelRow = this.quantizedImage.GetPixelRowSpan(row); ReadOnlySpan<byte> indexedPixelRow = this.quantizedImage.DangerousGetRowSpan(row);
indexedPixelRow.CopyTo(indexedPixels.Slice(indexedPixelsRowIdx * width, width)); indexedPixelRow.CopyTo(indexedPixels.Slice(indexedPixelsRowIdx * width, width));
indexedPixelsRowIdx++; indexedPixelsRowIdx++;
} }

2
src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

@ -395,7 +395,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int widthBytes = width * 4; int widthBytes = width * 4;
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
Span<TPixel> rowSpan = image.GetPixelRowSpan(y); Span<TPixel> rowSpan = image.DangerousGetRowSpan(y);
Span<byte> rowBytes = bgraBytes.Slice(y * widthBytes, widthBytes); Span<byte> rowBytes = bgraBytes.Slice(y * widthBytes, widthBytes);
PixelOperations<TPixel>.Instance.ToBgra32Bytes(this.configuration, rowSpan, rowBytes, width); PixelOperations<TPixel>.Instance.ToBgra32Bytes(this.configuration, rowSpan, rowBytes, width);
if (!nonOpaque) if (!nonOpaque)

6
src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs

@ -46,8 +46,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int rowIndex; int rowIndex;
for (rowIndex = 0; rowIndex < height - 1; rowIndex += 2) for (rowIndex = 0; rowIndex < height - 1; rowIndex += 2)
{ {
Span<TPixel> rowSpan = image.GetPixelRowSpan(rowIndex); Span<TPixel> rowSpan = image.DangerousGetRowSpan(rowIndex);
Span<TPixel> nextRowSpan = image.GetPixelRowSpan(rowIndex + 1); Span<TPixel> nextRowSpan = image.DangerousGetRowSpan(rowIndex + 1);
PixelOperations<TPixel>.Instance.ToBgra32(configuration, rowSpan, bgraRow0); PixelOperations<TPixel>.Instance.ToBgra32(configuration, rowSpan, bgraRow0);
PixelOperations<TPixel>.Instance.ToBgra32(configuration, nextRowSpan, bgraRow1); PixelOperations<TPixel>.Instance.ToBgra32(configuration, nextRowSpan, bgraRow1);
@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Extra last row. // Extra last row.
if ((height & 1) != 0) if ((height & 1) != 0)
{ {
Span<TPixel> rowSpan = image.GetPixelRowSpan(rowIndex); Span<TPixel> rowSpan = image.DangerousGetRowSpan(rowIndex);
PixelOperations<TPixel>.Instance.ToBgra32(configuration, rowSpan, bgraRow0); PixelOperations<TPixel>.Instance.ToBgra32(configuration, rowSpan, bgraRow0);
ConvertRgbaToY(bgraRow0, y.Slice(rowIndex * width), width); ConvertRgbaToY(bgraRow0, y.Slice(rowIndex * width), width);

32
src/ImageSharp/ImageFrame{TPixel}.cs

@ -177,24 +177,6 @@ namespace SixLabors.ImageSharp
} }
} }
/// <summary>
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory
/// at row <paramref name="rowIndex"/> beginning from the first pixel on that row.
/// <para />
/// WARNING: Disposing or leaking the underlying image while still working with it's <see cref="Span{T}"/>
/// might lead to memory corruption.
/// </summary>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when row index is out of range.</exception>
public Span<TPixel> GetPixelRowSpan(int rowIndex)
{
Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex));
Guard.MustBeLessThan(rowIndex, this.Height, nameof(rowIndex));
return this.PixelBuffer.DangerousGetRowSpan(rowIndex);
}
/// <summary> /// <summary>
/// Execute <paramref name="processPixels"/> to process image pixels in a safe and efficient manner. /// Execute <paramref name="processPixels"/> to process image pixels in a safe and efficient manner.
/// </summary> /// </summary>
@ -418,7 +400,7 @@ namespace SixLabors.ImageSharp
} }
var target = new ImageFrame<TPixel2>(configuration, this.Width, this.Height, this.Metadata.DeepClone()); var target = new ImageFrame<TPixel2>(configuration, this.Width, this.Height, this.Metadata.DeepClone());
var operation = new RowIntervalOperation<TPixel2>(this, target, configuration); var operation = new RowIntervalOperation<TPixel2>(this.PixelBuffer, target.PixelBuffer, configuration);
ParallelRowIterator.IterateRowIntervals( ParallelRowIterator.IterateRowIntervals(
configuration, configuration,
@ -472,14 +454,14 @@ namespace SixLabors.ImageSharp
private readonly struct RowIntervalOperation<TPixel2> : IRowIntervalOperation private readonly struct RowIntervalOperation<TPixel2> : IRowIntervalOperation
where TPixel2 : unmanaged, IPixel<TPixel2> where TPixel2 : unmanaged, IPixel<TPixel2>
{ {
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly ImageFrame<TPixel2> target; private readonly Buffer2D<TPixel2> target;
private readonly Configuration configuration; private readonly Configuration configuration;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperation( public RowIntervalOperation(
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
ImageFrame<TPixel2> target, Buffer2D<TPixel2> target,
Configuration configuration) Configuration configuration)
{ {
this.source = source; this.source = source;
@ -493,8 +475,8 @@ namespace SixLabors.ImageSharp
{ {
for (int y = rows.Min; y < rows.Max; y++) for (int y = rows.Min; y < rows.Max; y++)
{ {
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y); Span<TPixel> sourceRow = this.source.DangerousGetRowSpan(y);
Span<TPixel2> targetRow = this.target.GetPixelRowSpan(y); Span<TPixel2> targetRow = this.target.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.To(this.configuration, sourceRow, targetRow); PixelOperations<TPixel>.Instance.To(this.configuration, sourceRow, targetRow);
} }
} }

2
src/ImageSharp/Image{TPixel}.cs

@ -310,7 +310,7 @@ namespace SixLabors.ImageSharp
/// <param name="rowIndex">The row.</param> /// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns> /// <returns>The <see cref="Span{TPixel}"/></returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when row index is out of range.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown when row index is out of range.</exception>
public Span<TPixel> GetPixelRowSpan(int rowIndex) public Span<TPixel> DangerousGetRowSpan(int rowIndex)
{ {
Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex)); Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex));
Guard.MustBeLessThan(rowIndex, this.Height, nameof(rowIndex)); Guard.MustBeLessThan(rowIndex, this.Height, nameof(rowIndex));

2
src/ImageSharp/IndexedImageFrame{TPixel}.cs

@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp
/// <param name="rowIndex">The row index in the pixel buffer.</param> /// <param name="rowIndex">The row index in the pixel buffer.</param>
/// <returns>The pixel row as a <see cref="ReadOnlySpan{T}"/>.</returns> /// <returns>The pixel row as a <see cref="ReadOnlySpan{T}"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public ReadOnlySpan<byte> GetPixelRowSpan(int rowIndex) public ReadOnlySpan<byte> DangerousGetRowSpan(int rowIndex)
=> this.GetWritablePixelRowSpanUnsafe(rowIndex); => this.GetWritablePixelRowSpanUnsafe(rowIndex);
/// <summary> /// <summary>

2
src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.CommonFactoryMethods.cs

@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Memory
/// This is the default. Should be good for most use cases. /// This is the default. Should be good for most use cases.
/// </summary> /// </summary>
/// <returns>The memory manager.</returns> /// <returns>The memory manager.</returns>
public static new ArrayPoolMemoryAllocator CreateDefault() public static ArrayPoolMemoryAllocator CreateDefault()
{ {
return new ArrayPoolMemoryAllocator( return new ArrayPoolMemoryAllocator(
DefaultMaxPooledBufferSizeInBytes, DefaultMaxPooledBufferSizeInBytes,

2
src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Memory.Internals
// The worst thing that could happen is that a VERY poorly written user code holding a Span<TPixel> on the stack, // The worst thing that could happen is that a VERY poorly written user code holding a Span<TPixel> on the stack,
// while loosing the reference to Image<TPixel> (or disposing it) may write to an unrelated ArrayPool array. // while loosing the reference to Image<TPixel> (or disposing it) may write to an unrelated ArrayPool array.
// This is an unlikely scenario we mitigate by a warning in GetPixelRowSpan(i) APIs. // This is an unlikely scenario we mitigate by a warning in DangerousGetRowSpan(i) APIs.
#pragma warning disable CA2015 // Adding a finalizer to a type derived from MemoryManager<T> may permit memory to be freed while it is still in use by a Span<T> #pragma warning disable CA2015 // Adding a finalizer to a type derived from MemoryManager<T> may permit memory to be freed while it is still in use by a Span<T>
~SharedArrayPoolBuffer() => this.Dispose(false); ~SharedArrayPoolBuffer() => this.Dispose(false);
#pragma warning restore #pragma warning restore

2
src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.Buffer{T}.cs

@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Memory.Internals
// A VERY poorly written user code holding a Span<TPixel> on the stack, // A VERY poorly written user code holding a Span<TPixel> on the stack,
// while loosing the reference to Image<TPixel> (or disposing it) may write to (now unrelated) pool buffer, // while loosing the reference to Image<TPixel> (or disposing it) may write to (now unrelated) pool buffer,
// or cause memory corruption if the underlying UmnanagedMemoryHandle has been released. // or cause memory corruption if the underlying UmnanagedMemoryHandle has been released.
// This is an unlikely scenario we mitigate a warning in GetPixelRowSpan(i) APIs. // This is an unlikely scenario we mitigate a warning in DangerousGetRowSpan(i) APIs.
#pragma warning disable CA2015 // Adding a finalizer to a type derived from MemoryManager<T> may permit memory to be freed while it is still in use by a Span<T> #pragma warning disable CA2015 // Adding a finalizer to a type derived from MemoryManager<T> may permit memory to be freed while it is still in use by a Span<T>
~FinalizableBuffer() => this.Dispose(false); ~FinalizableBuffer() => this.Dispose(false);
#pragma warning restore #pragma warning restore

14
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -111,10 +111,16 @@ namespace SixLabors.ImageSharp.Memory
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param> /// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="Size{T}"/> of the buffer</returns> /// <returns>The <see cref="Size{T}"/> of the buffer</returns>
internal static Size Size<T>(this Buffer2D<T> buffer) internal static Size Size<T>(this Buffer2D<T> buffer)
where T : struct where T : struct =>
{ new(buffer.Width, buffer.Height);
return new Size(buffer.Width, buffer.Height);
} /// <summary>
/// Gets the bounds of the buffer.
/// </summary>
/// <returns>The <see cref="Rectangle"/></returns>
internal static Rectangle Bounds<T>(this Buffer2D<T> buffer)
where T : struct =>
new(0, 0, buffer.Width, buffer.Height);
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void CheckColumnRegionsDoNotOverlap<T>( private static void CheckColumnRegionsDoNotOverlap<T>(

4
src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing
using (IMemoryOwner<L8> tempRow = configuration.MemoryAllocator.Allocate<L8>(source.Width)) using (IMemoryOwner<L8> tempRow = configuration.MemoryAllocator.Allocate<L8>(source.Width))
{ {
Span<L8> tempSpan = tempRow.GetSpan(); Span<L8> tempSpan = tempRow.GetSpan();
Span<TPixel> sourceRow = source.GetPixelRowSpan(0); Span<TPixel> sourceRow = source.DangerousGetRowSpan(0);
Span<ulong> destRow = intImage.DangerousGetRowSpan(0); Span<ulong> destRow = intImage.DangerousGetRowSpan(0);
PixelOperations<TPixel>.Instance.ToL8(configuration, sourceRow, tempSpan); PixelOperations<TPixel>.Instance.ToL8(configuration, sourceRow, tempSpan);
@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing
// All other rows // All other rows
for (int y = 1; y < endY; y++) for (int y = 1; y < endY; y++)
{ {
sourceRow = source.GetPixelRowSpan(y); sourceRow = source.DangerousGetRowSpan(y);
destRow = intImage.DangerousGetRowSpan(y); destRow = intImage.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToL8(configuration, sourceRow, tempSpan); PixelOperations<TPixel>.Instance.ToL8(configuration, sourceRow, tempSpan);

12
src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs

@ -52,6 +52,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
// ClusterSize defines the size of cluster to used to check for average. Tweaked to support up to 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' // ClusterSize defines the size of cluster to used to check for average. Tweaked to support up to 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1'
byte clusterSize = (byte)Math.Truncate((width / 16f) - 1); byte clusterSize = (byte)Math.Truncate((width / 16f) - 1);
Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;
// Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data. // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data.
using (Buffer2D<ulong> intImage = this.Configuration.MemoryAllocator.Allocate2D<ulong>(width, height)) using (Buffer2D<ulong> intImage = this.Configuration.MemoryAllocator.Allocate2D<ulong>(width, height))
{ {
@ -61,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
ulong sum = 0; ulong sum = 0;
for (int y = startY; y < endY; y++) for (int y = startY; y < endY; y++)
{ {
Span<TPixel> row = source.GetPixelRowSpan(y); Span<TPixel> row = sourceBuffer.DangerousGetRowSpan(y);
ref TPixel rowRef = ref MemoryMarshal.GetReference(row); ref TPixel rowRef = ref MemoryMarshal.GetReference(row);
ref TPixel color = ref Unsafe.Add(ref rowRef, x); ref TPixel color = ref Unsafe.Add(ref rowRef, x);
color.ToRgba32(ref rgb); color.ToRgba32(ref rgb);
@ -79,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
} }
} }
var operation = new RowOperation(intersect, source, intImage, upper, lower, thresholdLimit, clusterSize, startX, endX, startY); var operation = new RowOperation(intersect, source.PixelBuffer, intImage, upper, lower, thresholdLimit, clusterSize, startX, endX, startY);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
configuration, configuration,
intersect, intersect,
@ -90,7 +92,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
private readonly struct RowOperation : IRowOperation private readonly struct RowOperation : IRowOperation
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly Buffer2D<ulong> intImage; private readonly Buffer2D<ulong> intImage;
private readonly TPixel upper; private readonly TPixel upper;
private readonly TPixel lower; private readonly TPixel lower;
@ -103,7 +105,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation( public RowOperation(
Rectangle bounds, Rectangle bounds,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
Buffer2D<ulong> intImage, Buffer2D<ulong> intImage,
TPixel upper, TPixel upper,
TPixel lower, TPixel lower,
@ -130,7 +132,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
public void Invoke(int y) public void Invoke(int y)
{ {
Rgba32 rgb = default; Rgba32 rgb = default;
Span<TPixel> pixelRow = this.source.GetPixelRowSpan(y); Span<TPixel> pixelRow = this.source.DangerousGetRowSpan(y);
for (int x = this.startX; x < this.endX; x++) for (int x = this.startX; x < this.endX; x++)
{ {

9
src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Binarization namespace SixLabors.ImageSharp.Processing.Processors.Binarization
@ -41,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
var operation = new RowOperation( var operation = new RowOperation(
interest.X, interest.X,
source, source.PixelBuffer,
upper, upper,
lower, lower,
threshold, threshold,
@ -59,7 +60,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
/// </summary> /// </summary>
private readonly struct RowOperation : IRowOperation<Rgb24> private readonly struct RowOperation : IRowOperation<Rgb24>
{ {
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly TPixel upper; private readonly TPixel upper;
private readonly TPixel lower; private readonly TPixel lower;
private readonly byte threshold; private readonly byte threshold;
@ -70,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation( public RowOperation(
int startX, int startX,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
TPixel upper, TPixel upper,
TPixel lower, TPixel lower,
byte threshold, byte threshold,
@ -93,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
TPixel upper = this.upper; TPixel upper = this.upper;
TPixel lower = this.lower; TPixel lower = this.lower;
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length); Span<TPixel> rowSpan = this.source.DangerousGetRowSpan(y).Slice(this.startX, span.Length);
PixelOperations<TPixel>.Instance.ToRgb24(this.configuration, rowSpan, span); PixelOperations<TPixel>.Instance.ToRgb24(this.configuration, rowSpan, span);
switch (this.mode) switch (this.mode)

12
src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs

@ -5,6 +5,7 @@ using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -105,14 +106,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
int offsetY = bounds.Top; int offsetY = bounds.Top;
int offsetX = bounds.Left; int offsetX = bounds.Left;
float scale = quantizer.Options.DitherScale; float scale = quantizer.Options.DitherScale;
Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;
for (int y = bounds.Top; y < bounds.Bottom; y++) for (int y = bounds.Top; y < bounds.Bottom; y++)
{ {
// Unsafe optimizations undone temporarily. // Unsafe optimizations undone temporarily.
// Sporadic local AccessViolationException indicates possible indexing bug. // Sporadic local AccessViolationException indicates possible indexing bug.
// ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); // ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(source.DangerousGetRowSpan(y));
// ref byte destinationRowRef = ref MemoryMarshal.GetReference(destination.GetWritablePixelRowSpanUnsafe(y - offsetY)); // ref byte destinationRowRef = ref MemoryMarshal.GetReference(destination.GetWritablePixelRowSpanUnsafe(y - offsetY));
Span<TPixel> sourceSpan = source.GetPixelRowSpan(y); Span<TPixel> sourceSpan = sourceBuffer.DangerousGetRowSpan(y);
Span<byte> destSpan = destination.GetWritablePixelRowSpanUnsafe(y - offsetY); Span<byte> destSpan = destination.GetWritablePixelRowSpanUnsafe(y - offsetY);
for (int x = bounds.Left; x < bounds.Right; x++) for (int x = bounds.Left; x < bounds.Right; x++)
@ -140,10 +142,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
ThrowDefaultInstance(); ThrowDefaultInstance();
} }
Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;
float scale = processor.DitherScale; float scale = processor.DitherScale;
for (int y = bounds.Top; y < bounds.Bottom; y++) for (int y = bounds.Top; y < bounds.Bottom; y++)
{ {
ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(sourceBuffer.DangerousGetRowSpan(y));
for (int x = bounds.Left; x < bounds.Right; x++) for (int x = bounds.Left; x < bounds.Right; x++)
{ {
ref TPixel sourcePixel = ref Unsafe.Add(ref sourceRowRef, x); ref TPixel sourcePixel = ref Unsafe.Add(ref sourceRowRef, x);
@ -177,6 +180,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
int offset = this.offset; int offset = this.offset;
DenseMatrix<float> matrix = this.matrix; DenseMatrix<float> matrix = this.matrix;
Buffer2D<TPixel> imageBuffer = image.PixelBuffer;
// Loop through and distribute the error amongst neighboring pixels. // Loop through and distribute the error amongst neighboring pixels.
for (int row = 0, targetY = y; row < matrix.Rows; row++, targetY++) for (int row = 0, targetY = y; row < matrix.Rows; row++, targetY++)
@ -186,7 +190,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
continue; continue;
} }
Span<TPixel> rowSpan = image.GetPixelRowSpan(targetY); Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(targetY);
for (int col = 0; col < matrix.Columns; col++) for (int col = 0; col < matrix.Columns; col++)
{ {

7
src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -118,10 +119,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
int spread = CalculatePaletteSpread(destination.Palette.Length); int spread = CalculatePaletteSpread(destination.Palette.Length);
float scale = quantizer.Options.DitherScale; float scale = quantizer.Options.DitherScale;
Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;
for (int y = bounds.Top; y < bounds.Bottom; y++) for (int y = bounds.Top; y < bounds.Bottom; y++)
{ {
ReadOnlySpan<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(bounds.X, bounds.Width); ReadOnlySpan<TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(y).Slice(bounds.X, bounds.Width);
Span<byte> destRow = destination.GetWritablePixelRowSpanUnsafe(y - bounds.Y).Slice(0, sourceRow.Length); Span<byte> destRow = destination.GetWritablePixelRowSpanUnsafe(y - bounds.Y).Slice(0, sourceRow.Length);
for (int x = 0; x < sourceRow.Length; x++) for (int x = 0; x < sourceRow.Length; x++)
@ -148,10 +150,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
int spread = CalculatePaletteSpread(processor.Palette.Length); int spread = CalculatePaletteSpread(processor.Palette.Length);
float scale = processor.DitherScale; float scale = processor.DitherScale;
Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;
for (int y = bounds.Top; y < bounds.Bottom; y++) for (int y = bounds.Top; y < bounds.Bottom; y++)
{ {
Span<TPixel> row = source.GetPixelRowSpan(y).Slice(bounds.X, bounds.Width); Span<TPixel> row = sourceBuffer.DangerousGetRowSpan(y).Slice(bounds.X, bounds.Width);
for (int x = 0; x < row.Length; x++) for (int x = 0; x < row.Length; x++)
{ {

18
src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs

@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
"Cannot draw image because the source image does not overlap the target image."); "Cannot draw image because the source image does not overlap the target image.");
} }
var operation = new RowOperation(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity); var operation = new RowOperation(source.PixelBuffer, targetImage.Frames.RootFrame.PixelBuffer, blender, configuration, minX, width, locationY, targetX, this.Opacity);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
configuration, configuration,
workingRect, workingRect,
@ -111,8 +111,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// </summary> /// </summary>
private readonly struct RowOperation : IRowOperation private readonly struct RowOperation : IRowOperation
{ {
private readonly ImageFrame<TPixelBg> sourceFrame; private readonly Buffer2D<TPixelBg> source;
private readonly Image<TPixelFg> targetImage; private readonly Buffer2D<TPixelFg> target;
private readonly PixelBlender<TPixelBg> blender; private readonly PixelBlender<TPixelBg> blender;
private readonly Configuration configuration; private readonly Configuration configuration;
private readonly int minX; private readonly int minX;
@ -123,8 +123,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation( public RowOperation(
ImageFrame<TPixelBg> sourceFrame, Buffer2D<TPixelBg> source,
Image<TPixelFg> targetImage, Buffer2D<TPixelFg> target,
PixelBlender<TPixelBg> blender, PixelBlender<TPixelBg> blender,
Configuration configuration, Configuration configuration,
int minX, int minX,
@ -133,8 +133,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int targetX, int targetX,
float opacity) float opacity)
{ {
this.sourceFrame = sourceFrame; this.source = source;
this.targetImage = targetImage; this.target = target;
this.blender = blender; this.blender = blender;
this.configuration = configuration; this.configuration = configuration;
this.minX = minX; this.minX = minX;
@ -148,8 +148,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y) public void Invoke(int y)
{ {
Span<TPixelBg> background = this.sourceFrame.GetPixelRowSpan(y).Slice(this.minX, this.width); Span<TPixelBg> background = this.source.DangerousGetRowSpan(y).Slice(this.minX, this.width);
Span<TPixelFg> foreground = this.targetImage.GetPixelRowSpan(y - this.locationY).Slice(this.targetX, this.width); Span<TPixelFg> foreground = this.target.DangerousGetRowSpan(y - this.locationY).Slice(this.targetX, this.width);
this.blender.Blend<TPixelFg>(this.configuration, background, background, foreground, this.opacity); this.blender.Blend<TPixelFg>(this.configuration, background, background, foreground, this.opacity);
} }
} }

10
src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs

@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
source.CopyTo(targetPixels); source.CopyTo(targetPixels);
var operation = new RowIntervalOperation(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels); var operation = new RowIntervalOperation(this.SourceRectangle, targetPixels, source.PixelBuffer, this.Configuration, brushSize >> 1, this.definition.Levels);
ParallelRowIterator.IterateRowIntervals( ParallelRowIterator.IterateRowIntervals(
this.Configuration, this.Configuration,
this.SourceRectangle, this.SourceRectangle,
@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly Buffer2D<TPixel> targetPixels; private readonly Buffer2D<TPixel> targetPixels;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly Configuration configuration; private readonly Configuration configuration;
private readonly int radius; private readonly int radius;
private readonly int levels; private readonly int levels;
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
public RowIntervalOperation( public RowIntervalOperation(
Rectangle bounds, Rectangle bounds,
Buffer2D<TPixel> targetPixels, Buffer2D<TPixel> targetPixels,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
Configuration configuration, Configuration configuration,
int radius, int radius,
int levels) int levels)
@ -120,7 +120,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
for (int y = rows.Min; y < rows.Max; y++) for (int y = rows.Min; y < rows.Max; y++)
{ {
Span<TPixel> sourceRowPixelSpan = this.source.GetPixelRowSpan(y); Span<TPixel> sourceRowPixelSpan = this.source.DangerousGetRowSpan(y);
Span<TPixel> sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(this.bounds.X, this.bounds.Width); Span<TPixel> sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(this.bounds.X, this.bounds.Width);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span); PixelOperations<TPixel>.Instance.ToVector4(this.configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span);
@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
int offsetY = y + fyr; int offsetY = y + fyr;
offsetY = Numerics.Clamp(offsetY, 0, maxY); offsetY = Numerics.Clamp(offsetY, 0, maxY);
Span<TPixel> sourceOffsetRow = this.source.GetPixelRowSpan(offsetY); Span<TPixel> sourceOffsetRow = this.source.DangerousGetRowSpan(offsetY);
for (int fx = 0; fx <= this.radius; fx++) for (int fx = 0; fx <= this.radius; fx++)
{ {

8
src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs

@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
protected override void OnFrameApply(ImageFrame<TPixel> source) protected override void OnFrameApply(ImageFrame<TPixel> source)
{ {
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
var operation = new RowOperation(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate); var operation = new RowOperation(interest.X, source.PixelBuffer, this.Configuration, this.modifiers, this.rowDelegate);
ParallelRowIterator.IterateRows<RowOperation, Vector4>( ParallelRowIterator.IterateRows<RowOperation, Vector4>(
this.Configuration, this.Configuration,
@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
private readonly struct RowOperation : IRowOperation<Vector4> private readonly struct RowOperation : IRowOperation<Vector4>
{ {
private readonly int startX; private readonly int startX;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly Configuration configuration; private readonly Configuration configuration;
private readonly PixelConversionModifiers modifiers; private readonly PixelConversionModifiers modifiers;
private readonly TDelegate rowProcessor; private readonly TDelegate rowProcessor;
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation( public RowOperation(
int startX, int startX,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
Configuration configuration, Configuration configuration,
PixelConversionModifiers modifiers, PixelConversionModifiers modifiers,
in TDelegate rowProcessor) in TDelegate rowProcessor)
@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span) public void Invoke(int y, Span<Vector4> span)
{ {
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length); Span<TPixel> rowSpan = this.source.DangerousGetRowSpan(y).Slice(this.startX, span.Length);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers); PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers);
// Run the user defined pixel shader to the current row of pixels // Run the user defined pixel shader to the current row of pixels

9
src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Effects namespace SixLabors.ImageSharp.Processing.Processors.Effects
@ -48,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
Parallel.ForEach( Parallel.ForEach(
range, range,
this.Configuration.GetParallelOptions(), this.Configuration.GetParallelOptions(),
new RowOperation(interest, size, source).Invoke); new RowOperation(interest, size, source.PixelBuffer).Invoke);
} }
private readonly struct RowOperation private readonly struct RowOperation
@ -60,13 +61,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
private readonly int maxYIndex; private readonly int maxYIndex;
private readonly int size; private readonly int size;
private readonly int radius; private readonly int radius;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation( public RowOperation(
Rectangle bounds, Rectangle bounds,
int size, int size,
ImageFrame<TPixel> source) Buffer2D<TPixel> source)
{ {
this.minX = bounds.X; this.minX = bounds.X;
this.maxX = bounds.Right; this.maxX = bounds.Right;
@ -81,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y) public void Invoke(int y)
{ {
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(Math.Min(y + this.radius, this.maxYIndex)); Span<TPixel> rowSpan = this.source.DangerousGetRowSpan(Math.Min(y + this.radius, this.maxYIndex));
for (int x = this.minX; x < this.maxX; x += this.size) for (int x = this.minX; x < this.maxX; x += this.size)
{ {

8
src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs

@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
protected override void OnFrameApply(ImageFrame<TPixel> source) protected override void OnFrameApply(ImageFrame<TPixel> source)
{ {
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
var operation = new RowOperation(interest.X, source, this.definition.Matrix, this.Configuration); var operation = new RowOperation(interest.X, source.PixelBuffer, this.definition.Matrix, this.Configuration);
ParallelRowIterator.IterateRows<RowOperation, Vector4>( ParallelRowIterator.IterateRows<RowOperation, Vector4>(
this.Configuration, this.Configuration,
@ -50,14 +50,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
private readonly struct RowOperation : IRowOperation<Vector4> private readonly struct RowOperation : IRowOperation<Vector4>
{ {
private readonly int startX; private readonly int startX;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly ColorMatrix matrix; private readonly ColorMatrix matrix;
private readonly Configuration configuration; private readonly Configuration configuration;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation( public RowOperation(
int startX, int startX,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
ColorMatrix matrix, ColorMatrix matrix,
Configuration configuration) Configuration configuration)
{ {
@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span) public void Invoke(int y, Span<Vector4> span)
{ {
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length); Span<TPixel> rowSpan = this.source.DangerousGetRowSpan(y).Slice(this.startX, span.Length);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span, PixelConversionModifiers.Scale); PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span, PixelConversionModifiers.Scale);
ColorNumerics.Transform(span, ref Unsafe.AsRef(this.matrix)); ColorNumerics.Transform(span, ref Unsafe.AsRef(this.matrix));

9
src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs

@ -6,6 +6,7 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Filters namespace SixLabors.ImageSharp.Processing.Processors.Filters
@ -25,20 +26,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
{ {
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
var operation = new OpaqueRowOperation(this.Configuration, source, interest); var operation = new OpaqueRowOperation(this.Configuration, source.PixelBuffer, interest);
ParallelRowIterator.IterateRows<OpaqueRowOperation, Vector4>(this.Configuration, interest, in operation); ParallelRowIterator.IterateRows<OpaqueRowOperation, Vector4>(this.Configuration, interest, in operation);
} }
private readonly struct OpaqueRowOperation : IRowOperation<Vector4> private readonly struct OpaqueRowOperation : IRowOperation<Vector4>
{ {
private readonly Configuration configuration; private readonly Configuration configuration;
private readonly ImageFrame<TPixel> target; private readonly Buffer2D<TPixel> target;
private readonly Rectangle bounds; private readonly Rectangle bounds;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public OpaqueRowOperation( public OpaqueRowOperation(
Configuration configuration, Configuration configuration,
ImageFrame<TPixel> target, Buffer2D<TPixel> target,
Rectangle bounds) Rectangle bounds)
{ {
this.configuration = configuration; this.configuration = configuration;
@ -50,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span) public void Invoke(int y, Span<Vector4> span)
{ {
Span<TPixel> targetRowSpan = this.target.GetPixelRowSpan(y).Slice(this.bounds.X); Span<TPixel> targetRowSpan = this.target.DangerousGetRowSpan(y).Slice(this.bounds.X);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span, PixelConversionModifiers.Scale); PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span, PixelConversionModifiers.Scale);
ref Vector4 baseRef = ref MemoryMarshal.GetReference(span); ref Vector4 baseRef = ref MemoryMarshal.GetReference(span);

44
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs

@ -80,37 +80,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
yStart += tileHeight; yStart += tileHeight;
} }
var operation = new RowIntervalOperation(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source); var operation = new RowIntervalOperation(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source.PixelBuffer);
ParallelRowIterator.IterateRowIntervals( ParallelRowIterator.IterateRowIntervals(
this.Configuration, this.Configuration,
new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count), new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count),
in operation); in operation);
// Fix left column // Fix left column
ProcessBorderColumn(source, cdfData, 0, sourceHeight, this.Tiles, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); ProcessBorderColumn(source.PixelBuffer, cdfData, 0, sourceHeight, this.Tiles, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels);
// Fix right column // Fix right column
int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth; int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth;
ProcessBorderColumn(source, cdfData, this.Tiles - 1, sourceHeight, this.Tiles, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); ProcessBorderColumn(source.PixelBuffer, cdfData, this.Tiles - 1, sourceHeight, this.Tiles, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels);
// Fix top row // Fix top row
ProcessBorderRow(source, cdfData, 0, sourceWidth, this.Tiles, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); ProcessBorderRow(source.PixelBuffer, cdfData, 0, sourceWidth, this.Tiles, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels);
// Fix bottom row // Fix bottom row
int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight; int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight;
ProcessBorderRow(source, cdfData, this.Tiles - 1, sourceWidth, this.Tiles, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); ProcessBorderRow(source.PixelBuffer, cdfData, this.Tiles - 1, sourceWidth, this.Tiles, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels);
// Left top corner // Left top corner
ProcessCornerTile(source, cdfData, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); ProcessCornerTile(source.PixelBuffer, cdfData, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels);
// Left bottom corner // Left bottom corner
ProcessCornerTile(source, cdfData, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); ProcessCornerTile(source.PixelBuffer, cdfData, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels);
// Right top corner // Right top corner
ProcessCornerTile(source, cdfData, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); ProcessCornerTile(source.PixelBuffer, cdfData, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels);
// Right bottom corner // Right bottom corner
ProcessCornerTile(source, cdfData, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); ProcessCornerTile(source.PixelBuffer, cdfData, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels);
} }
} }
@ -130,7 +130,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// or 65536 for 16-bit grayscale images. /// or 65536 for 16-bit grayscale images.
/// </param> /// </param>
private static void ProcessCornerTile( private static void ProcessCornerTile(
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
CdfTileData cdfData, CdfTileData cdfData,
int cdfX, int cdfX,
int cdfY, int cdfY,
@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{ {
for (int dy = yStart; dy < yEnd; dy++) for (int dy = yStart; dy < yEnd; dy++)
{ {
Span<TPixel> rowSpan = source.GetPixelRowSpan(dy); Span<TPixel> rowSpan = source.DangerousGetRowSpan(dy);
for (int dx = xStart; dx < xEnd; dx++) for (int dx = xStart; dx < xEnd; dx++)
{ {
ref TPixel pixel = ref rowSpan[dx]; ref TPixel pixel = ref rowSpan[dx];
@ -168,7 +168,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// or 65536 for 16-bit grayscale images. /// or 65536 for 16-bit grayscale images.
/// </param> /// </param>
private static void ProcessBorderColumn( private static void ProcessBorderColumn(
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
CdfTileData cdfData, CdfTileData cdfData,
int cdfX, int cdfX,
int sourceHeight, int sourceHeight,
@ -188,7 +188,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int tileY = 0; int tileY = 0;
for (int dy = y; dy < yLimit; dy++) for (int dy = y; dy < yLimit; dy++)
{ {
Span<TPixel> rowSpan = source.GetPixelRowSpan(dy); Span<TPixel> rowSpan = source.DangerousGetRowSpan(dy);
for (int dx = xStart; dx < xEnd; dx++) for (int dx = xStart; dx < xEnd; dx++)
{ {
ref TPixel pixel = ref rowSpan[dx]; ref TPixel pixel = ref rowSpan[dx];
@ -220,7 +220,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// or 65536 for 16-bit grayscale images. /// or 65536 for 16-bit grayscale images.
/// </param> /// </param>
private static void ProcessBorderRow( private static void ProcessBorderRow(
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
CdfTileData cdfData, CdfTileData cdfData,
int cdfY, int cdfY,
int sourceWidth, int sourceWidth,
@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{ {
for (int dy = yStart; dy < yEnd; dy++) for (int dy = yStart; dy < yEnd; dy++)
{ {
Span<TPixel> rowSpan = source.GetPixelRowSpan(dy); Span<TPixel> rowSpan = source.DangerousGetRowSpan(dy);
int tileX = 0; int tileX = 0;
int xLimit = Math.Min(x + tileWidth, sourceWidth - 1); int xLimit = Math.Min(x + tileWidth, sourceWidth - 1);
for (int dx = x; dx < xLimit; dx++) for (int dx = x; dx < xLimit; dx++)
@ -373,7 +373,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly int tileCount; private readonly int tileCount;
private readonly int halfTileWidth; private readonly int halfTileWidth;
private readonly int luminanceLevels; private readonly int luminanceLevels;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly int sourceWidth; private readonly int sourceWidth;
private readonly int sourceHeight; private readonly int sourceHeight;
@ -386,7 +386,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int tileCount, int tileCount,
int halfTileWidth, int halfTileWidth,
int luminanceLevels, int luminanceLevels,
ImageFrame<TPixel> source) Buffer2D<TPixel> source)
{ {
this.cdfData = cdfData; this.cdfData = cdfData;
this.tileYStartPositions = tileYStartPositions; this.tileYStartPositions = tileYStartPositions;
@ -419,7 +419,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int xEnd = Math.Min(x + this.tileWidth, this.sourceWidth); int xEnd = Math.Min(x + this.tileWidth, this.sourceWidth);
for (int dy = y; dy < yEnd; dy++) for (int dy = y; dy < yEnd; dy++)
{ {
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(dy); Span<TPixel> rowSpan = this.source.DangerousGetRowSpan(dy);
int tileX = 0; int tileX = 0;
for (int dx = x; dx < xEnd; dx++) for (int dx = x; dx < xEnd; dx++)
{ {
@ -516,7 +516,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
this.tileWidth, this.tileWidth,
this.tileHeight, this.tileHeight,
this.luminanceLevels, this.luminanceLevels,
source); source.PixelBuffer);
ParallelRowIterator.IterateRowIntervals( ParallelRowIterator.IterateRowIntervals(
this.configuration, this.configuration,
@ -560,7 +560,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly int tileWidth; private readonly int tileWidth;
private readonly int tileHeight; private readonly int tileHeight;
private readonly int luminanceLevels; private readonly int luminanceLevels;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly int sourceWidth; private readonly int sourceWidth;
private readonly int sourceHeight; private readonly int sourceHeight;
@ -574,7 +574,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int tileWidth, int tileWidth,
int tileHeight, int tileHeight,
int luminanceLevels, int luminanceLevels,
ImageFrame<TPixel> source) Buffer2D<TPixel> source)
{ {
this.processor = processor; this.processor = processor;
this.allocator = allocator; this.allocator = allocator;
@ -615,7 +615,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int xlimit = Math.Min(x + this.tileWidth, this.sourceWidth); int xlimit = Math.Min(x + this.tileWidth, this.sourceWidth);
for (int dy = y; dy < endY; dy++) for (int dy = y; dy < endY; dy++)
{ {
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(dy); Span<TPixel> rowSpan = this.source.DangerousGetRowSpan(dy);
for (int dx = x; dx < xlimit; dx++) for (int dx = x; dx < xlimit; dx++)
{ {
int luminance = GetLuminance(rowSpan[dx], this.luminanceLevels); int luminance = GetLuminance(rowSpan[dx], this.luminanceLevels);

14
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs

@ -189,7 +189,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
y = source.Height - diff - 1; y = source.Height - diff - 1;
} }
// Special cases for the left and the right border where GetPixelRowSpan can not be used. // Special cases for the left and the right border where DangerousGetRowSpan can not be used.
if (x < 0) if (x < 0)
{ {
rowPixels.Clear(); rowPixels.Clear();
@ -224,7 +224,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
return; return;
} }
this.CopyPixelRowFast(source, rowPixels, x, y, tileWidth, configuration); this.CopyPixelRowFast(source.PixelBuffer, rowPixels, x, y, tileWidth, configuration);
} }
/// <summary> /// <summary>
@ -238,13 +238,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private void CopyPixelRowFast( private void CopyPixelRowFast(
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
Span<Vector4> rowPixels, Span<Vector4> rowPixels,
int x, int x,
int y, int y,
int tileWidth, int tileWidth,
Configuration configuration) Configuration configuration)
=> PixelOperations<TPixel>.Instance.ToVector4(configuration, source.GetPixelRowSpan(y).Slice(start: x, length: tileWidth), rowPixels); => PixelOperations<TPixel>.Instance.ToVector4(configuration, source.DangerousGetRowSpan(y).Slice(start: x, length: tileWidth), rowPixels);
/// <summary> /// <summary>
/// Adds a column of grey values to the histogram. /// Adds a column of grey values to the histogram.
@ -356,7 +356,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{ {
if (this.useFastPath) if (this.useFastPath)
{ {
this.processor.CopyPixelRowFast(this.source, pixelRow, x - this.swInfos.HalfTileWidth, dy, this.swInfos.TileWidth, this.configuration); this.processor.CopyPixelRowFast(this.source.PixelBuffer, pixelRow, x - this.swInfos.HalfTileWidth, dy, this.swInfos.TileWidth, this.configuration);
} }
else else
{ {
@ -390,7 +390,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
// Remove top most row from the histogram, mirroring rows which exceeds the borders. // Remove top most row from the histogram, mirroring rows which exceeds the borders.
if (this.useFastPath) if (this.useFastPath)
{ {
this.processor.CopyPixelRowFast(this.source, pixelRow, x - this.swInfos.HalfTileWidth, y - this.swInfos.HalfTileWidth, this.swInfos.TileWidth, this.configuration); this.processor.CopyPixelRowFast(this.source.PixelBuffer, pixelRow, x - this.swInfos.HalfTileWidth, y - this.swInfos.HalfTileWidth, this.swInfos.TileWidth, this.configuration);
} }
else else
{ {
@ -402,7 +402,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
// Add new bottom row to the histogram, mirroring rows which exceeds the borders. // Add new bottom row to the histogram, mirroring rows which exceeds the borders.
if (this.useFastPath) if (this.useFastPath)
{ {
this.processor.CopyPixelRowFast(this.source, pixelRow, x - this.swInfos.HalfTileWidth, y + this.swInfos.HalfTileWidth, this.swInfos.TileWidth, this.configuration); this.processor.CopyPixelRowFast(this.source.PixelBuffer, pixelRow, x - this.swInfos.HalfTileWidth, y + this.swInfos.HalfTileWidth, this.swInfos.TileWidth, this.configuration);
} }
else else
{ {

16
src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
using IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean); using IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels. // Build the histogram of the grayscale levels.
var grayscaleOperation = new GrayscaleLevelsRowOperation(interest, histogramBuffer, source, this.LuminanceLevels); var grayscaleOperation = new GrayscaleLevelsRowOperation(interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
this.Configuration, this.Configuration,
interest, interest,
@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
// Apply the cdf to each pixel of the image // Apply the cdf to each pixel of the image
var cdfOperation = new CdfApplicationRowOperation(interest, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin); var cdfOperation = new CdfApplicationRowOperation(interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
this.Configuration, this.Configuration,
interest, interest,
@ -90,14 +90,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly IMemoryOwner<int> histogramBuffer; private readonly IMemoryOwner<int> histogramBuffer;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly int luminanceLevels; private readonly int luminanceLevels;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public GrayscaleLevelsRowOperation( public GrayscaleLevelsRowOperation(
Rectangle bounds, Rectangle bounds,
IMemoryOwner<int> histogramBuffer, IMemoryOwner<int> histogramBuffer,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
int luminanceLevels) int luminanceLevels)
{ {
this.bounds = bounds; this.bounds = bounds;
@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
public void Invoke(int y) public void Invoke(int y)
{ {
ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan()); ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan());
Span<TPixel> pixelRow = this.source.GetPixelRowSpan(y); Span<TPixel> pixelRow = this.source.DangerousGetRowSpan(y);
int levels = this.luminanceLevels; int levels = this.luminanceLevels;
for (int x = 0; x < this.bounds.Width; x++) for (int x = 0; x < this.bounds.Width; x++)
@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly IMemoryOwner<int> cdfBuffer; private readonly IMemoryOwner<int> cdfBuffer;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly int luminanceLevels; private readonly int luminanceLevels;
private readonly float numberOfPixelsMinusCdfMin; private readonly float numberOfPixelsMinusCdfMin;
@ -144,7 +144,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
public CdfApplicationRowOperation( public CdfApplicationRowOperation(
Rectangle bounds, Rectangle bounds,
IMemoryOwner<int> cdfBuffer, IMemoryOwner<int> cdfBuffer,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
int luminanceLevels, int luminanceLevels,
float numberOfPixelsMinusCdfMin) float numberOfPixelsMinusCdfMin)
{ {
@ -165,7 +165,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
public void Invoke(int y) public void Invoke(int y)
{ {
ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan()); ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
Span<TPixel> pixelRow = this.source.GetPixelRowSpan(y); Span<TPixel> pixelRow = this.source.DangerousGetRowSpan(y);
int levels = this.luminanceLevels; int levels = this.luminanceLevels;
float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin; float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin;

8
src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs

@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(graphicsOptions); PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(graphicsOptions);
var operation = new RowOperation(configuration, interest, blender, amount, colors, source); var operation = new RowOperation(configuration, interest, blender, amount, colors, source.PixelBuffer);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
configuration, configuration,
interest, interest,
@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly PixelBlender<TPixel> blender; private readonly PixelBlender<TPixel> blender;
private readonly IMemoryOwner<float> amount; private readonly IMemoryOwner<float> amount;
private readonly IMemoryOwner<TPixel> colors; private readonly IMemoryOwner<TPixel> colors;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation( public RowOperation(
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
PixelBlender<TPixel> blender, PixelBlender<TPixel> blender,
IMemoryOwner<float> amount, IMemoryOwner<float> amount,
IMemoryOwner<TPixel> colors, IMemoryOwner<TPixel> colors,
ImageFrame<TPixel> source) Buffer2D<TPixel> source)
{ {
this.configuration = configuration; this.configuration = configuration;
this.bounds = bounds; this.bounds = bounds;
@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
public void Invoke(int y) public void Invoke(int y)
{ {
Span<TPixel> destination = Span<TPixel> destination =
this.source.GetPixelRowSpan(y) this.source.DangerousGetRowSpan(y)
.Slice(this.bounds.X, this.bounds.Width); .Slice(this.bounds.X, this.bounds.Width);
// Switch color & destination in the 2nd and 3rd places because we are // Switch color & destination in the 2nd and 3rd places because we are

8
src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs

@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
using IMemoryOwner<TPixel> rowColors = allocator.Allocate<TPixel>(interest.Width); using IMemoryOwner<TPixel> rowColors = allocator.Allocate<TPixel>(interest.Width);
rowColors.GetSpan().Fill(glowColor); rowColors.GetSpan().Fill(glowColor);
var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source); var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source.PixelBuffer);
ParallelRowIterator.IterateRows<RowOperation, float>( ParallelRowIterator.IterateRows<RowOperation, float>(
configuration, configuration,
interest, interest,
@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly float maxDistance; private readonly float maxDistance;
private readonly float blendPercent; private readonly float blendPercent;
private readonly IMemoryOwner<TPixel> colors; private readonly IMemoryOwner<TPixel> colors;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation( public RowOperation(
@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
Vector2 center, Vector2 center,
float maxDistance, float maxDistance,
float blendPercent, float blendPercent,
ImageFrame<TPixel> source) Buffer2D<TPixel> source)
{ {
this.configuration = configuration; this.configuration = configuration;
this.bounds = bounds; this.bounds = bounds;
@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
span[i] = Numerics.Clamp(this.blendPercent * (1 - (.95F * (distance / this.maxDistance))), 0, 1F); span[i] = Numerics.Clamp(this.blendPercent * (1 - (.95F * (distance / this.maxDistance))), 0, 1F);
} }
Span<TPixel> destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width); Span<TPixel> destination = this.source.DangerousGetRowSpan(y).Slice(this.bounds.X, this.bounds.Width);
this.blender.Blend( this.blender.Blend(
this.configuration, this.configuration,

8
src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs

@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
using IMemoryOwner<TPixel> rowColors = allocator.Allocate<TPixel>(interest.Width); using IMemoryOwner<TPixel> rowColors = allocator.Allocate<TPixel>(interest.Width);
rowColors.GetSpan().Fill(vignetteColor); rowColors.GetSpan().Fill(vignetteColor);
var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source); var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source.PixelBuffer);
ParallelRowIterator.IterateRows<RowOperation, float>( ParallelRowIterator.IterateRows<RowOperation, float>(
configuration, configuration,
interest, interest,
@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly float maxDistance; private readonly float maxDistance;
private readonly float blendPercent; private readonly float blendPercent;
private readonly IMemoryOwner<TPixel> colors; private readonly IMemoryOwner<TPixel> colors;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation( public RowOperation(
@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
Vector2 center, Vector2 center,
float maxDistance, float maxDistance,
float blendPercent, float blendPercent,
ImageFrame<TPixel> source) Buffer2D<TPixel> source)
{ {
this.configuration = configuration; this.configuration = configuration;
this.bounds = bounds; this.bounds = bounds;
@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
span[i] = Numerics.Clamp(this.blendPercent * (.9F * (distance / this.maxDistance)), 0, 1F); span[i] = Numerics.Clamp(this.blendPercent * (.9F * (distance / this.maxDistance)), 0, 1F);
} }
Span<TPixel> destination = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width); Span<TPixel> destination = this.source.DangerousGetRowSpan(y).Slice(this.bounds.X, this.bounds.Width);
this.blender.Blend( this.blender.Blend(
this.configuration, this.configuration,

5
src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs

@ -44,11 +44,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
ReadOnlySpan<TPixel> paletteSpan = quantized.Palette.Span; ReadOnlySpan<TPixel> paletteSpan = quantized.Palette.Span;
int offsetY = interest.Top; int offsetY = interest.Top;
int offsetX = interest.Left; int offsetX = interest.Left;
Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;
for (int y = interest.Y; y < interest.Height; y++) for (int y = interest.Y; y < interest.Height; y++)
{ {
Span<TPixel> row = source.GetPixelRowSpan(y); Span<TPixel> row = sourceBuffer.DangerousGetRowSpan(y);
ReadOnlySpan<byte> quantizedRow = quantized.GetPixelRowSpan(y - offsetY); ReadOnlySpan<byte> quantizedRow = quantized.DangerousGetRowSpan(y - offsetY);
for (int x = interest.Left; x < interest.Right; x++) for (int x = interest.Left; x < interest.Right; x++)
{ {

3
src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs

@ -122,6 +122,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
IDither dither = quantizer.Options.Dither; IDither dither = quantizer.Options.Dither;
Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;
if (dither is null) if (dither is null)
{ {
@ -130,7 +131,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
for (int y = bounds.Y; y < bounds.Height; y++) for (int y = bounds.Y; y < bounds.Height; y++)
{ {
Span<TPixel> sourceRow = source.GetPixelRowSpan(y); Span<TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(y);
Span<byte> destinationRow = destination.GetWritablePixelRowSpanUnsafe(y - offsetY); Span<byte> destinationRow = destination.GetWritablePixelRowSpanUnsafe(y - offsetY);
for (int x = bounds.Left; x < bounds.Right; x++) for (int x = bounds.Left; x < bounds.Right; x++)

12
src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs

@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
ParallelExecutionSettings parallelSettings = ParallelExecutionSettings parallelSettings =
ParallelExecutionSettings.FromConfiguration(this.Configuration).MultiplyMinimumPixelsPerTask(4); ParallelExecutionSettings.FromConfiguration(this.Configuration).MultiplyMinimumPixelsPerTask(4);
var operation = new RowOperation(bounds, source, destination); var operation = new RowOperation(bounds, source.PixelBuffer, destination.PixelBuffer);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
bounds, bounds,
@ -65,8 +65,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly struct RowOperation : IRowOperation private readonly struct RowOperation : IRowOperation
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly ImageFrame<TPixel> destination; private readonly Buffer2D<TPixel> destination;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RowOperation"/> struct. /// Initializes a new instance of the <see cref="RowOperation"/> struct.
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current instance.</param> /// <param name="source">The source <see cref="Image{TPixel}"/> for the current instance.</param>
/// <param name="destination">The destination <see cref="Image{TPixel}"/> for the current instance.</param> /// <param name="destination">The destination <see cref="Image{TPixel}"/> for the current instance.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation(Rectangle bounds, ImageFrame<TPixel> source, ImageFrame<TPixel> destination) public RowOperation(Rectangle bounds, Buffer2D<TPixel> source, Buffer2D<TPixel> destination)
{ {
this.bounds = bounds; this.bounds = bounds;
this.source = source; this.source = source;
@ -86,8 +86,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y) public void Invoke(int y)
{ {
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y).Slice(this.bounds.Left); Span<TPixel> sourceRow = this.source.DangerousGetRowSpan(y).Slice(this.bounds.Left);
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(y - this.bounds.Top); Span<TPixel> targetRow = this.destination.DangerousGetRowSpan(y - this.bounds.Top);
sourceRow.Slice(0, this.bounds.Width).CopyTo(targetRow); sourceRow.Slice(0, this.bounds.Width).CopyTo(targetRow);
} }
} }

39
src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs

@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if (sampler is NearestNeighborResampler) if (sampler is NearestNeighborResampler)
{ {
var nnOperation = new NNAffineOperation(source, destination, matrix); var nnOperation = new NNAffineOperation(source.PixelBuffer, destination.PixelBuffer, matrix);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
configuration, configuration,
destination.Bounds(), destination.Bounds(),
@ -84,8 +84,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
var operation = new AffineOperation<TResampler>( var operation = new AffineOperation<TResampler>(
configuration, configuration,
source, source.PixelBuffer,
destination, destination.PixelBuffer,
in sampler, in sampler,
matrix); matrix);
@ -97,15 +97,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly struct NNAffineOperation : IRowOperation private readonly struct NNAffineOperation : IRowOperation
{ {
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly ImageFrame<TPixel> destination; private readonly Buffer2D<TPixel> destination;
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly Matrix3x2 matrix; private readonly Matrix3x2 matrix;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public NNAffineOperation( public NNAffineOperation(
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
ImageFrame<TPixel> destination, Buffer2D<TPixel> destination,
Matrix3x2 matrix) Matrix3x2 matrix)
{ {
this.source = source; this.source = source;
@ -117,8 +117,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y) public void Invoke(int y)
{ {
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer; Span<TPixel> destRow = this.destination.DangerousGetRowSpan(y);
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
for (int x = 0; x < destRow.Length; x++) for (int x = 0; x < destRow.Length; x++)
{ {
@ -128,7 +127,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if (this.bounds.Contains(px, py)) if (this.bounds.Contains(px, py))
{ {
destRow[x] = sourceBuffer.GetElementUnsafe(px, py); destRow[x] = this.source.GetElementUnsafe(px, py);
} }
} }
} }
@ -138,8 +137,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
where TResampler : struct, IResampler where TResampler : struct, IResampler
{ {
private readonly Configuration configuration; private readonly Configuration configuration;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly ImageFrame<TPixel> destination; private readonly Buffer2D<TPixel> destination;
private readonly TResampler sampler; private readonly TResampler sampler;
private readonly Matrix3x2 matrix; private readonly Matrix3x2 matrix;
private readonly float yRadius; private readonly float yRadius;
@ -148,8 +147,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public AffineOperation( public AffineOperation(
Configuration configuration, Configuration configuration,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
ImageFrame<TPixel> destination, Buffer2D<TPixel> destination,
in TResampler sampler, in TResampler sampler,
Matrix3x2 matrix) Matrix3x2 matrix)
{ {
@ -186,11 +185,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int maxY = this.source.Height - 1; int maxY = this.source.Height - 1;
int maxX = this.source.Width - 1; int maxX = this.source.Width - 1;
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer;
for (int y = rows.Min; y < rows.Max; y++) for (int y = rows.Min; y < rows.Max; y++)
{ {
Span<TPixel> rowSpan = this.destination.GetPixelRowSpan(y); Span<TPixel> rowSpan = this.destination.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4( PixelOperations<TPixel>.Instance.ToVector4(
this.configuration, this.configuration,
rowSpan, rowSpan,
@ -222,7 +219,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
float xWeight = sampler.GetValue(xK - pX); float xWeight = sampler.GetValue(xK - pX);
Vector4 current = sourceBuffer.GetElementUnsafe(xK, yK).ToScaledVector4(); Vector4 current = this.source.GetElementUnsafe(xK, yK).ToScaledVector4();
Numerics.Premultiply(ref current); Numerics.Premultiply(ref current);
sum += current * xWeight * yWeight; sum += current * xWeight * yWeight;
} }
@ -251,11 +248,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int maxY = this.source.Height - 1; int maxY = this.source.Height - 1;
int maxX = this.source.Width - 1; int maxX = this.source.Width - 1;
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer;
for (int y = rows.Min; y < rows.Max; y++) for (int y = rows.Min; y < rows.Max; y++)
{ {
Span<TPixel> rowSpan = this.destination.GetPixelRowSpan(y); Span<TPixel> rowSpan = this.destination.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4( PixelOperations<TPixel>.Instance.ToVector4(
this.configuration, this.configuration,
rowSpan, rowSpan,
@ -287,7 +282,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
float xWeight = sampler.GetValue(xK - pX); float xWeight = sampler.GetValue(xK - pX);
Vector4 current = sourceBuffer.GetElementUnsafe(xK, yK).ToScaledVector4(); Vector4 current = this.source.GetElementUnsafe(xK, yK).ToScaledVector4();
Numerics.Premultiply(ref current); Numerics.Premultiply(ref current);
sum += current * xWeight * yWeight; sum += current * xWeight * yWeight;
} }

17
src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs

@ -5,6 +5,7 @@ using System;
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@ -38,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
// No default needed as we have already set the pixels. // No default needed as we have already set the pixels.
case FlipMode.Vertical: case FlipMode.Vertical:
this.FlipX(source, this.Configuration); this.FlipX(source.PixelBuffer, this.Configuration);
break; break;
case FlipMode.Horizontal: case FlipMode.Horizontal:
this.FlipY(source, this.Configuration); this.FlipY(source, this.Configuration);
@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary> /// </summary>
/// <param name="source">The source image to apply the process to.</param> /// <param name="source">The source image to apply the process to.</param>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void FlipX(ImageFrame<TPixel> source, Configuration configuration) private void FlipX(Buffer2D<TPixel> source, Configuration configuration)
{ {
int height = source.Height; int height = source.Height;
using IMemoryOwner<TPixel> tempBuffer = configuration.MemoryAllocator.Allocate<TPixel>(source.Width); using IMemoryOwner<TPixel> tempBuffer = configuration.MemoryAllocator.Allocate<TPixel>(source.Width);
@ -60,8 +61,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
for (int yTop = 0; yTop < height / 2; yTop++) for (int yTop = 0; yTop < height / 2; yTop++)
{ {
int yBottom = height - yTop - 1; int yBottom = height - yTop - 1;
Span<TPixel> topRow = source.GetPixelRowSpan(yBottom); Span<TPixel> topRow = source.DangerousGetRowSpan(yBottom);
Span<TPixel> bottomRow = source.GetPixelRowSpan(yTop); Span<TPixel> bottomRow = source.DangerousGetRowSpan(yTop);
topRow.CopyTo(temp); topRow.CopyTo(temp);
bottomRow.CopyTo(topRow); bottomRow.CopyTo(topRow);
temp.CopyTo(bottomRow); temp.CopyTo(bottomRow);
@ -75,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void FlipY(ImageFrame<TPixel> source, Configuration configuration) private void FlipY(ImageFrame<TPixel> source, Configuration configuration)
{ {
var operation = new RowOperation(source); var operation = new RowOperation(source.PixelBuffer);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
configuration, configuration,
source.Bounds(), source.Bounds(),
@ -84,13 +85,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly struct RowOperation : IRowOperation private readonly struct RowOperation : IRowOperation
{ {
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation(ImageFrame<TPixel> source) => this.source = source; public RowOperation(Buffer2D<TPixel> source) => this.source = source;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y) => this.source.GetPixelRowSpan(y).Reverse(); public void Invoke(int y) => this.source.DangerousGetRowSpan(y).Reverse();
} }
} }
} }

39
src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs

@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if (sampler is NearestNeighborResampler) if (sampler is NearestNeighborResampler)
{ {
var nnOperation = new NNProjectiveOperation(source, destination, matrix); var nnOperation = new NNProjectiveOperation(source.PixelBuffer, destination.PixelBuffer, matrix);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
configuration, configuration,
destination.Bounds(), destination.Bounds(),
@ -83,8 +83,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
var operation = new ProjectiveOperation<TResampler>( var operation = new ProjectiveOperation<TResampler>(
configuration, configuration,
source, source.PixelBuffer,
destination, destination.PixelBuffer,
in sampler, in sampler,
matrix); matrix);
@ -96,15 +96,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly struct NNProjectiveOperation : IRowOperation private readonly struct NNProjectiveOperation : IRowOperation
{ {
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly ImageFrame<TPixel> destination; private readonly Buffer2D<TPixel> destination;
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly Matrix4x4 matrix; private readonly Matrix4x4 matrix;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public NNProjectiveOperation( public NNProjectiveOperation(
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
ImageFrame<TPixel> destination, Buffer2D<TPixel> destination,
Matrix4x4 matrix) Matrix4x4 matrix)
{ {
this.source = source; this.source = source;
@ -116,8 +116,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y) public void Invoke(int y)
{ {
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer; Span<TPixel> destRow = this.destination.DangerousGetRowSpan(y);
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
for (int x = 0; x < destRow.Length; x++) for (int x = 0; x < destRow.Length; x++)
{ {
@ -127,7 +126,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if (this.bounds.Contains(px, py)) if (this.bounds.Contains(px, py))
{ {
destRow[x] = sourceBuffer.GetElementUnsafe(px, py); destRow[x] = this.source.GetElementUnsafe(px, py);
} }
} }
} }
@ -137,8 +136,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
where TResampler : struct, IResampler where TResampler : struct, IResampler
{ {
private readonly Configuration configuration; private readonly Configuration configuration;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly ImageFrame<TPixel> destination; private readonly Buffer2D<TPixel> destination;
private readonly TResampler sampler; private readonly TResampler sampler;
private readonly Matrix4x4 matrix; private readonly Matrix4x4 matrix;
private readonly float yRadius; private readonly float yRadius;
@ -147,8 +146,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public ProjectiveOperation( public ProjectiveOperation(
Configuration configuration, Configuration configuration,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
ImageFrame<TPixel> destination, Buffer2D<TPixel> destination,
in TResampler sampler, in TResampler sampler,
Matrix4x4 matrix) Matrix4x4 matrix)
{ {
@ -185,11 +184,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int maxY = this.source.Height - 1; int maxY = this.source.Height - 1;
int maxX = this.source.Width - 1; int maxX = this.source.Width - 1;
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer;
for (int y = rows.Min; y < rows.Max; y++) for (int y = rows.Min; y < rows.Max; y++)
{ {
Span<TPixel> rowSpan = this.destination.GetPixelRowSpan(y); Span<TPixel> rowSpan = this.destination.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4( PixelOperations<TPixel>.Instance.ToVector4(
this.configuration, this.configuration,
rowSpan, rowSpan,
@ -221,7 +218,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
float xWeight = sampler.GetValue(xK - pX); float xWeight = sampler.GetValue(xK - pX);
Vector4 current = sourceBuffer.GetElementUnsafe(xK, yK).ToScaledVector4(); Vector4 current = this.source.GetElementUnsafe(xK, yK).ToScaledVector4();
Numerics.Premultiply(ref current); Numerics.Premultiply(ref current);
sum += current * xWeight * yWeight; sum += current * xWeight * yWeight;
} }
@ -250,11 +247,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int maxY = this.source.Height - 1; int maxY = this.source.Height - 1;
int maxX = this.source.Width - 1; int maxX = this.source.Width - 1;
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer;
for (int y = rows.Min; y < rows.Max; y++) for (int y = rows.Min; y < rows.Max; y++)
{ {
Span<TPixel> rowSpan = this.destination.GetPixelRowSpan(y); Span<TPixel> rowSpan = this.destination.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4( PixelOperations<TPixel>.Instance.ToVector4(
this.configuration, this.configuration,
rowSpan, rowSpan,
@ -286,7 +281,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
float xWeight = sampler.GetValue(xK - pX); float xWeight = sampler.GetValue(xK - pX);
Vector4 current = sourceBuffer.GetElementUnsafe(xK, yK).ToScaledVector4(); Vector4 current = this.source.GetElementUnsafe(xK, yK).ToScaledVector4();
Numerics.Premultiply(ref current); Numerics.Premultiply(ref current);
sum += current * xWeight * yWeight; sum += current * xWeight * yWeight;
} }

38
src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs

@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void Rotate180(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration) private void Rotate180(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{ {
var operation = new Rotate180RowOperation(source.Width, source.Height, source, destination); var operation = new Rotate180RowOperation(source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
configuration, configuration,
source.Bounds(), source.Bounds(),
@ -146,7 +146,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void Rotate270(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration) private void Rotate270(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{ {
var operation = new Rotate270RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source, destination); var operation = new Rotate270RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer);
ParallelRowIterator.IterateRowIntervals( ParallelRowIterator.IterateRowIntervals(
configuration, configuration,
source.Bounds(), source.Bounds(),
@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void Rotate90(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration) private void Rotate90(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{ {
var operation = new Rotate90RowOperation(destination.Bounds(), source.Width, source.Height, source, destination); var operation = new Rotate90RowOperation(destination.Bounds(), source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
configuration, configuration,
source.Bounds(), source.Bounds(),
@ -172,15 +172,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
private readonly int width; private readonly int width;
private readonly int height; private readonly int height;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly ImageFrame<TPixel> destination; private readonly Buffer2D<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Rotate180RowOperation( public Rotate180RowOperation(
int width, int width,
int height, int height,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
ImageFrame<TPixel> destination) Buffer2D<TPixel> destination)
{ {
this.width = width; this.width = width;
this.height = height; this.height = height;
@ -191,8 +191,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y) public void Invoke(int y)
{ {
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y); Span<TPixel> sourceRow = this.source.DangerousGetRowSpan(y);
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(this.height - y - 1); Span<TPixel> targetRow = this.destination.DangerousGetRowSpan(this.height - y - 1);
for (int x = 0; x < this.width; x++) for (int x = 0; x < this.width; x++)
{ {
@ -206,16 +206,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly int width; private readonly int width;
private readonly int height; private readonly int height;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly ImageFrame<TPixel> destination; private readonly Buffer2D<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Rotate270RowIntervalOperation( public Rotate270RowIntervalOperation(
Rectangle bounds, Rectangle bounds,
int width, int width,
int height, int height,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
ImageFrame<TPixel> destination) Buffer2D<TPixel> destination)
{ {
this.bounds = bounds; this.bounds = bounds;
this.width = width; this.width = width;
@ -229,7 +229,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
for (int y = rows.Min; y < rows.Max; y++) for (int y = rows.Min; y < rows.Max; y++)
{ {
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y); Span<TPixel> sourceRow = this.source.DangerousGetRowSpan(y);
for (int x = 0; x < this.width; x++) for (int x = 0; x < this.width; x++)
{ {
int newX = this.height - y - 1; int newX = this.height - y - 1;
@ -250,16 +250,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly int width; private readonly int width;
private readonly int height; private readonly int height;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly ImageFrame<TPixel> destination; private readonly Buffer2D<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Rotate90RowOperation( public Rotate90RowOperation(
Rectangle bounds, Rectangle bounds,
int width, int width,
int height, int height,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
ImageFrame<TPixel> destination) Buffer2D<TPixel> destination)
{ {
this.bounds = bounds; this.bounds = bounds;
this.width = width; this.width = width;
@ -271,7 +271,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y) public void Invoke(int y)
{ {
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y); Span<TPixel> sourceRow = this.source.DangerousGetRowSpan(y);
int newX = this.height - y - 1; int newX = this.height - y - 1;
for (int x = 0; x < this.width; x++) for (int x = 0; x < this.width; x++)
{ {

16
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs

@ -155,8 +155,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
interest, interest,
widthFactor, widthFactor,
heightFactor, heightFactor,
source, source.PixelBuffer,
destination); destination.PixelBuffer);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
configuration, configuration,
@ -223,8 +223,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly Rectangle interest; private readonly Rectangle interest;
private readonly float widthFactor; private readonly float widthFactor;
private readonly float heightFactor; private readonly float heightFactor;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
private readonly ImageFrame<TPixel> destination; private readonly Buffer2D<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public NNRowOperation( public NNRowOperation(
@ -233,8 +233,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Rectangle interest, Rectangle interest,
float widthFactor, float widthFactor,
float heightFactor, float heightFactor,
ImageFrame<TPixel> source, Buffer2D<TPixel> source,
ImageFrame<TPixel> destination) Buffer2D<TPixel> destination)
{ {
this.sourceBounds = sourceBounds; this.sourceBounds = sourceBounds;
this.destinationBounds = destinationBounds; this.destinationBounds = destinationBounds;
@ -256,8 +256,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int destRight = this.interest.Right; int destRight = this.interest.Right;
// Y coordinates of source points // Y coordinates of source points
Span<TPixel> sourceRow = this.source.GetPixelRowSpan((int)(((y - destOriginY) * this.heightFactor) + sourceY)); Span<TPixel> sourceRow = this.source.DangerousGetRowSpan((int)(((y - destOriginY) * this.heightFactor) + sourceY));
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(y); Span<TPixel> targetRow = this.destination.DangerousGetRowSpan(y);
for (int x = destLeft; x < destRight; x++) for (int x = destLeft; x < destRight; x++)
{ {

4
src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@ -27,9 +28,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
Point p = default; Point p = default;
Point newPoint; Point newPoint;
Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;
for (p.Y = 0; p.Y < source.Height; p.Y++) for (p.Y = 0; p.Y < source.Height; p.Y++)
{ {
Span<TPixel> rowSpan = source.GetPixelRowSpan(p.Y); Span<TPixel> rowSpan = sourceBuffer.DangerousGetRowSpan(p.Y);
for (p.X = 0; p.X < source.Width; p.X++) for (p.X = 0; p.X < source.Width; p.X++)
{ {
newPoint = this.swizzler.Transform(p); newPoint = this.swizzler.Transform(p);

4
tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs

@ -153,7 +153,7 @@ namespace SixLabors.ImageSharp.Tests.Advanced
[WithBlankImages(1, 1, PixelTypes.Rgba32)] [WithBlankImages(1, 1, PixelTypes.Rgba32)]
[WithBlankImages(100, 111, PixelTypes.Rgba32)] [WithBlankImages(100, 111, PixelTypes.Rgba32)]
[WithBlankImages(400, 600, PixelTypes.Rgba32)] [WithBlankImages(400, 600, PixelTypes.Rgba32)]
public void GetPixelRowSpan_ShouldReferenceSpanOfMemory<TPixel>(TestImageProvider<TPixel> provider) public void DangerousGetRowSpan_ShouldReferenceSpanOfMemory<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(200); provider.LimitAllocatorBufferCapacity().InPixelsSqrt(200);
@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.Tests.Advanced
using Image<TPixel> image = provider.GetImage(); using Image<TPixel> image = provider.GetImage();
Memory<TPixel> memory = image.DangerousGetPixelRowMemory(image.Height - 1); Memory<TPixel> memory = image.DangerousGetPixelRowMemory(image.Height - 1);
Span<TPixel> span = image.GetPixelRowSpan(image.Height - 1); Span<TPixel> span = image.DangerousGetRowSpan(image.Height - 1);
Assert.True(span == memory.Span); Assert.True(span == memory.Span);
} }

4
tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

@ -413,7 +413,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
Rgba32 rgba32 = Color.Blue; Rgba32 rgba32 = Color.Blue;
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
System.Span<Rgba32> rowSpan = image.GetPixelRowSpan(y); System.Span<Rgba32> rowSpan = image.DangerousGetRowSpan(y);
// Half of the test image should be transparent. // Half of the test image should be transparent.
if (y > 25) if (y > 25)
@ -443,7 +443,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
for (int y = 0; y < actual.Height; y++) for (int y = 0; y < actual.Height; y++)
{ {
System.Span<Rgba32> rowSpan = actual.GetPixelRowSpan(y); System.Span<Rgba32> rowSpan = actual.DangerousGetRowSpan(y);
if (y > 25) if (y > 25)
{ {

2
tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs

@ -134,7 +134,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
int idx = 0; int idx = 0;
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
Span<TPixel> rowSpan = image.GetPixelRowSpan(y); Span<TPixel> rowSpan = image.DangerousGetRowSpan(y);
for (int x = 0; x < rowSpan.Length; x++) for (int x = 0; x < rowSpan.Length; x++)
{ {
bgra[idx++] = ToBgra32(rowSpan[x]).PackedValue; bgra[idx++] = ToBgra32(rowSpan[x]).PackedValue;

16
tests/ImageSharp.Tests/Image/ImageCloneTests.cs

@ -37,8 +37,8 @@ namespace SixLabors.ImageSharp.Tests
{ {
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
Span<Rgba32> row = image.GetPixelRowSpan(y); Span<Rgba32> row = image.DangerousGetRowSpan(y);
Span<Bgra32> rowClone = clone.GetPixelRowSpan(y); Span<Bgra32> rowClone = clone.DangerousGetRowSpan(y);
for (int x = 0; x < image.Width; x++) for (int x = 0; x < image.Width; x++)
{ {
@ -63,8 +63,8 @@ namespace SixLabors.ImageSharp.Tests
{ {
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
Span<Rgba32> row = image.GetPixelRowSpan(y); Span<Rgba32> row = image.DangerousGetRowSpan(y);
Span<Bgr24> rowClone = clone.GetPixelRowSpan(y); Span<Bgr24> rowClone = clone.DangerousGetRowSpan(y);
for (int x = 0; x < image.Width; x++) for (int x = 0; x < image.Width; x++)
{ {
@ -88,8 +88,8 @@ namespace SixLabors.ImageSharp.Tests
{ {
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
Span<Rgba32> row = image.GetPixelRowSpan(y); Span<Rgba32> row = image.DangerousGetRowSpan(y);
Span<Argb32> rowClone = clone.GetPixelRowSpan(y); Span<Argb32> rowClone = clone.DangerousGetRowSpan(y);
for (int x = 0; x < image.Width; x++) for (int x = 0; x < image.Width; x++)
{ {
@ -114,8 +114,8 @@ namespace SixLabors.ImageSharp.Tests
{ {
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
Span<Rgba32> row = image.GetPixelRowSpan(y); Span<Rgba32> row = image.DangerousGetRowSpan(y);
Span<Rgb24> rowClone = clone.GetPixelRowSpan(y); Span<Rgb24> rowClone = clone.DangerousGetRowSpan(y);
for (int x = 0; x < image.Width; x++) for (int x = 0; x < image.Width; x++)
{ {

12
tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs

@ -165,7 +165,7 @@ namespace SixLabors.ImageSharp.Tests
image.GetPixelMemoryGroup().Fill(bg); image.GetPixelMemoryGroup().Fill(bg);
for (var i = 10; i < 20; i++) for (var i = 10; i < 20; i++)
{ {
image.GetPixelRowSpan(i).Slice(10, 10).Fill(fg); image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg);
} }
} }
@ -200,7 +200,7 @@ namespace SixLabors.ImageSharp.Tests
image.GetPixelMemoryGroup().Fill(bg); image.GetPixelMemoryGroup().Fill(bg);
for (var i = 10; i < 20; i++) for (var i = 10; i < 20; i++)
{ {
image.GetPixelRowSpan(i).Slice(10, 10).Fill(fg); image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg);
} }
} }
@ -265,7 +265,7 @@ namespace SixLabors.ImageSharp.Tests
image.GetPixelMemoryGroup().Fill(bg); image.GetPixelMemoryGroup().Fill(bg);
for (var i = 10; i < 20; i++) for (var i = 10; i < 20; i++)
{ {
image.GetPixelRowSpan(i).Slice(10, 10).Fill(fg); image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg);
} }
} }
@ -334,7 +334,7 @@ namespace SixLabors.ImageSharp.Tests
image.GetPixelMemoryGroup().Fill(bg); image.GetPixelMemoryGroup().Fill(bg);
for (var i = 10; i < 20; i++) for (var i = 10; i < 20; i++)
{ {
image.GetPixelRowSpan(i).Slice(10, 10).Fill(fg); image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg);
} }
} }
@ -417,7 +417,7 @@ namespace SixLabors.ImageSharp.Tests
{ {
var arrayIndex = width * i; var arrayIndex = width * i;
Span<Rgba32> rowSpan = img.GetPixelRowSpan(i); Span<Rgba32> rowSpan = img.DangerousGetRowSpan(i);
ref Rgba32 r0 = ref rowSpan[0]; ref Rgba32 r0 = ref rowSpan[0];
ref Rgba32 r1 = ref array[arrayIndex]; ref Rgba32 r1 = ref array[arrayIndex];
@ -461,7 +461,7 @@ namespace SixLabors.ImageSharp.Tests
{ {
var arrayIndex = pixelSize * width * i; var arrayIndex = pixelSize * width * i;
Span<Rgba32> rowSpan = img.GetPixelRowSpan(i); Span<Rgba32> rowSpan = img.DangerousGetRowSpan(i);
ref Rgba32 r0 = ref rowSpan[0]; ref Rgba32 r0 = ref rowSpan[0];
ref Rgba32 r1 = ref Unsafe.As<byte, Rgba32>(ref array[arrayIndex]); ref Rgba32 r1 = ref Unsafe.As<byte, Rgba32>(ref array[arrayIndex]);

2
tests/ImageSharp.Tests/Image/ImageTests.cs

@ -271,7 +271,7 @@ namespace SixLabors.ImageSharp.Tests
// Image<TPixel> // Image<TPixel>
Assert.Throws<ObjectDisposedException>(() => { var res = image.Clone(this.configuration); }); Assert.Throws<ObjectDisposedException>(() => { var res = image.Clone(this.configuration); });
Assert.Throws<ObjectDisposedException>(() => { var res = image.CloneAs<Rgba32>(this.configuration); }); Assert.Throws<ObjectDisposedException>(() => { var res = image.CloneAs<Rgba32>(this.configuration); });
Assert.Throws<ObjectDisposedException>(() => { var res = image.GetPixelRowSpan(default); }); Assert.Throws<ObjectDisposedException>(() => { var res = image.DangerousGetRowSpan(default); });
Assert.Throws<ObjectDisposedException>(() => { var res = image.DangerousTryGetSinglePixelMemory(out Memory<Rgba32> _); }); Assert.Throws<ObjectDisposedException>(() => { var res = image.DangerousTryGetSinglePixelMemory(out Memory<Rgba32> _); });
// Image // Image

4
tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs

@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests
using (IndexedImageFrame<TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds())) using (IndexedImageFrame<TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds()))
{ {
int index = this.GetTransparentIndex(quantized); int index = this.GetTransparentIndex(quantized);
Assert.Equal(index, quantized.GetPixelRowSpan(0)[0]); Assert.Equal(index, quantized.DangerousGetRowSpan(0)[0]);
} }
} }
} }
@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Tests
using (IndexedImageFrame<TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds())) using (IndexedImageFrame<TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds()))
{ {
int index = this.GetTransparentIndex(quantized); int index = this.GetTransparentIndex(quantized);
Assert.Equal(index, quantized.GetPixelRowSpan(0)[0]); Assert.Equal(index, quantized.DangerousGetRowSpan(0)[0]);
} }
} }
} }

16
tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
Assert.Equal(1, result.Height); Assert.Equal(1, result.Height);
Assert.Equal(Color.Black, (Color)result.Palette.Span[0]); Assert.Equal(Color.Black, (Color)result.Palette.Span[0]);
Assert.Equal(0, result.GetPixelRowSpan(0)[0]); Assert.Equal(0, result.DangerousGetRowSpan(0)[0]);
} }
[Fact] [Fact]
@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
Assert.Equal(1, result.Height); Assert.Equal(1, result.Height);
Assert.Equal(default, result.Palette.Span[0]); Assert.Equal(default, result.Palette.Span[0]);
Assert.Equal(0, result.GetPixelRowSpan(0)[0]); Assert.Equal(0, result.DangerousGetRowSpan(0)[0]);
} }
[Fact] [Fact]
@ -99,8 +99,8 @@ namespace SixLabors.ImageSharp.Tests.Quantization
int paletteCount = paletteSpan.Length - 1; int paletteCount = paletteSpan.Length - 1;
for (int y = 0; y < actualImage.Height; y++) for (int y = 0; y < actualImage.Height; y++)
{ {
Span<Rgba32> row = actualImage.GetPixelRowSpan(y); Span<Rgba32> row = actualImage.DangerousGetRowSpan(y);
ReadOnlySpan<byte> quantizedPixelSpan = result.GetPixelRowSpan(y); ReadOnlySpan<byte> quantizedPixelSpan = result.DangerousGetRowSpan(y);
for (int x = 0; x < actualImage.Width; x++) for (int x = 0; x < actualImage.Width; x++)
{ {
@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
Assert.True(image.GetPixelRowSpan(y).SequenceEqual(actualImage.GetPixelRowSpan(y))); Assert.True(image.DangerousGetRowSpan(y).SequenceEqual(actualImage.DangerousGetRowSpan(y)));
} }
} }
@ -166,8 +166,8 @@ namespace SixLabors.ImageSharp.Tests.Quantization
int paletteCount = paletteSpan.Length - 1; int paletteCount = paletteSpan.Length - 1;
for (int y = 0; y < actualImage.Height; y++) for (int y = 0; y < actualImage.Height; y++)
{ {
Span<Rgba32> row = actualImage.GetPixelRowSpan(y); Span<Rgba32> row = actualImage.DangerousGetRowSpan(y);
ReadOnlySpan<byte> quantizedPixelSpan = result.GetPixelRowSpan(y); ReadOnlySpan<byte> quantizedPixelSpan = result.DangerousGetRowSpan(y);
for (int x = 0; x < actualImage.Width; x++) for (int x = 0; x < actualImage.Width; x++)
{ {
@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
for (int y = 0; y < expectedImage.Height; y++) for (int y = 0; y < expectedImage.Height; y++)
{ {
Assert.True(expectedImage.GetPixelRowSpan(y).SequenceEqual(actualImage.GetPixelRowSpan(y))); Assert.True(expectedImage.DangerousGetRowSpan(y).SequenceEqual(actualImage.DangerousGetRowSpan(y)));
} }
} }
} }

7
tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
@ -29,11 +30,13 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
var differences = new List<PixelDifference>(); var differences = new List<PixelDifference>();
Configuration configuration = expected.GetConfiguration(); Configuration configuration = expected.GetConfiguration();
Buffer2D<TPixelA> expectedBuffer = expected.PixelBuffer;
Buffer2D<TPixelB> actualBuffer = actual.PixelBuffer;
for (int y = 0; y < actual.Height; y++) for (int y = 0; y < actual.Height; y++)
{ {
Span<TPixelA> aSpan = expected.GetPixelRowSpan(y); Span<TPixelA> aSpan = expectedBuffer.DangerousGetRowSpan(y);
Span<TPixelB> bSpan = actual.GetPixelRowSpan(y); Span<TPixelB> bSpan = actualBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixelA>.Instance.ToRgba64(configuration, aSpan, aBuffer); PixelOperations<TPixelA>.Instance.ToRgba64(configuration, aSpan, aBuffer);
PixelOperations<TPixelB>.Instance.ToRgba64(configuration, bSpan, bBuffer); PixelOperations<TPixelB>.Instance.ToRgba64(configuration, bSpan, bBuffer);

7
tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
@ -74,11 +75,13 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
var differences = new List<PixelDifference>(); var differences = new List<PixelDifference>();
Configuration configuration = expected.GetConfiguration(); Configuration configuration = expected.GetConfiguration();
Buffer2D<TPixelA> expectedBuffer = expected.PixelBuffer;
Buffer2D<TPixelB> actualBuffer = actual.PixelBuffer;
for (int y = 0; y < actual.Height; y++) for (int y = 0; y < actual.Height; y++)
{ {
Span<TPixelA> aSpan = expected.GetPixelRowSpan(y); Span<TPixelA> aSpan = expectedBuffer.DangerousGetRowSpan(y);
Span<TPixelB> bSpan = actual.GetPixelRowSpan(y); Span<TPixelB> bSpan = actualBuffer.DangerousGetRowSpan(y);
PixelOperations<TPixelA>.Instance.ToRgba64(configuration, aSpan, aBuffer); PixelOperations<TPixelA>.Instance.ToRgba64(configuration, aSpan, aBuffer);
PixelOperations<TPixelB>.Instance.ToRgba64(configuration, bSpan, bBuffer); PixelOperations<TPixelB>.Instance.ToRgba64(configuration, bSpan, bBuffer);

4
tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs

@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Tests
for (int y = 0; y < midY; y++) for (int y = 0; y < midY; y++)
{ {
Span<TPixel> row = result.GetPixelRowSpan(y); Span<TPixel> row = result.DangerousGetRowSpan(y);
row.Slice(0, midX).Fill(TopLeftColor); row.Slice(0, midX).Fill(TopLeftColor);
row.Slice(midX, this.Width - midX).Fill(TopRightColor); row.Slice(midX, this.Width - midX).Fill(TopRightColor);
@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests
for (int y = midY; y < this.Height; y++) for (int y = midY; y < this.Height; y++)
{ {
Span<TPixel> row = result.GetPixelRowSpan(y); Span<TPixel> row = result.DangerousGetRowSpan(y);
row.Slice(0, midX).Fill(BottomLeftColor); row.Slice(0, midX).Fill(BottomLeftColor);
row.Slice(midX, this.Width - midX).Fill(BottomRightColor); row.Slice(midX, this.Width - midX).Fill(BottomRightColor);

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

@ -47,14 +47,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
long destRowByteCount = w * sizeof(Bgra32); long destRowByteCount = w * sizeof(Bgra32);
Configuration configuration = image.GetConfiguration(); Configuration configuration = image.GetConfiguration();
image.ProcessPixelRows(accessor =>
using (IMemoryOwner<Bgra32> workBuffer = Configuration.Default.MemoryAllocator.Allocate<Bgra32>(w))
{ {
using IMemoryOwner<Bgra32> workBuffer = Configuration.Default.MemoryAllocator.Allocate<Bgra32>(w);
fixed (Bgra32* destPtr = &workBuffer.GetReference()) fixed (Bgra32* destPtr = &workBuffer.GetReference())
{ {
for (int y = 0; y < h; y++) for (int y = 0; y < h; y++)
{ {
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y); Span<TPixel> row = accessor.GetRowSpan(y);
byte* sourcePtr = sourcePtrBase + (data.Stride * y); byte* sourcePtr = sourcePtrBase + (data.Stride * y);
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
row); row);
} }
} }
} });
} }
finally finally
{ {
@ -106,6 +106,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
long destRowByteCount = w * sizeof(Bgr24); long destRowByteCount = w * sizeof(Bgr24);
Configuration configuration = image.GetConfiguration(); Configuration configuration = image.GetConfiguration();
Buffer2D<TPixel> imageBuffer = image.Frames.RootFrame.PixelBuffer;
using (IMemoryOwner<Bgr24> workBuffer = Configuration.Default.MemoryAllocator.Allocate<Bgr24>(w)) using (IMemoryOwner<Bgr24> workBuffer = Configuration.Default.MemoryAllocator.Allocate<Bgr24>(w))
{ {
@ -113,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{ {
for (int y = 0; y < h; y++) for (int y = 0; y < h; y++)
{ {
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y); Span<TPixel> row = imageBuffer.DangerousGetRowSpan(y);
byte* sourcePtr = sourcePtrBase + (data.Stride * y); byte* sourcePtr = sourcePtrBase + (data.Stride * y);
@ -144,24 +145,23 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
try try
{ {
byte* destPtrBase = (byte*)data.Scan0; byte* destPtrBase = (byte*)data.Scan0;
long destRowByteCount = data.Stride; long destRowByteCount = data.Stride;
long sourceRowByteCount = w * sizeof(Bgra32); long sourceRowByteCount = w * sizeof(Bgra32);
image.ProcessPixelRows(accessor =>
using (IMemoryOwner<Bgra32> workBuffer = image.GetConfiguration().MemoryAllocator.Allocate<Bgra32>(w))
{ {
using IMemoryOwner<Bgra32> workBuffer = image.GetConfiguration().MemoryAllocator.Allocate<Bgra32>(w);
fixed (Bgra32* sourcePtr = &workBuffer.GetReference()) fixed (Bgra32* sourcePtr = &workBuffer.GetReference())
{ {
for (int y = 0; y < h; y++) for (int y = 0; y < h; y++)
{ {
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y); Span<TPixel> row = accessor.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32(configuration, row, workBuffer.GetSpan()); PixelOperations<TPixel>.Instance.ToBgra32(configuration, row, workBuffer.GetSpan());
byte* destPtr = destPtrBase + (data.Stride * y); byte* destPtr = destPtrBase + (data.Stride * y);
Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
} }
} }
} });
} }
finally finally
{ {

8
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -738,7 +738,7 @@ namespace SixLabors.ImageSharp.Tests
Rectangle sourceRectangle = this.SourceRectangle; Rectangle sourceRectangle = this.SourceRectangle;
Configuration configuration = this.Configuration; Configuration configuration = this.Configuration;
var operation = new RowOperation(configuration, sourceRectangle, source); var operation = new RowOperation(configuration, sourceRectangle, source.PixelBuffer);
ParallelRowIterator.IterateRowIntervals<RowOperation, Vector4>( ParallelRowIterator.IterateRowIntervals<RowOperation, Vector4>(
configuration, configuration,
@ -750,9 +750,9 @@ namespace SixLabors.ImageSharp.Tests
{ {
private readonly Configuration configuration; private readonly Configuration configuration;
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly ImageFrame<TPixel> source; private readonly Buffer2D<TPixel> source;
public RowOperation(Configuration configuration, Rectangle bounds, ImageFrame<TPixel> source) public RowOperation(Configuration configuration, Rectangle bounds, Buffer2D<TPixel> source)
{ {
this.configuration = configuration; this.configuration = configuration;
this.bounds = bounds; this.bounds = bounds;
@ -763,7 +763,7 @@ namespace SixLabors.ImageSharp.Tests
{ {
for (int y = rows.Min; y < rows.Max; y++) for (int y = rows.Min; y < rows.Max; y++)
{ {
Span<TPixel> rowSpan = this.source.GetPixelRowSpan(y).Slice(this.bounds.Left, this.bounds.Width); Span<TPixel> rowSpan = this.source.DangerousGetRowSpan(y).Slice(this.bounds.Left, this.bounds.Width);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span, PixelConversionModifiers.Scale); PixelOperations<TPixel>.Instance.ToVector4(this.configuration, rowSpan, span, PixelConversionModifiers.Scale);
for (int i = 0; i < span.Length; i++) for (int i = 0; i < span.Length; i++)
{ {

Loading…
Cancel
Save