Browse Source

Remove Image.DangerousGetRowSpan

af/UniformUnmanagedMemoryPoolMemoryAllocator-02-MemoryGuards
Anton Firszov 4 years ago
parent
commit
ebe7b9c516
  1. 50
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  2. 26
      src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs
  3. 3
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  4. 11
      src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs
  5. 20
      src/ImageSharp/Image{TPixel}.cs
  6. 5
      src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs
  7. 17
      tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
  8. 48
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  9. 15
      tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs
  10. 63
      tests/ImageSharp.Tests/Image/ImageCloneTests.cs
  11. 71
      tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
  12. 1
      tests/ImageSharp.Tests/Image/ImageTests.cs
  13. 17
      tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs
  14. 62
      tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs
  15. 32
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs

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

@ -165,20 +165,24 @@ namespace SixLabors.ImageSharp.Formats.Png
private static void ClearTransparentPixels<TPixel>(Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
Rgba32 rgba32 = default;
for (int y = 0; y < image.Height; y++)
image.ProcessPixelRows(accessor =>
{
Span<TPixel> span = image.DangerousGetRowSpan(y);
for (int x = 0; x < image.Width; x++)
Rgba32 rgba32 = default;
for (int y = 0; y < accessor.Height; y++)
{
span[x].ToRgba32(ref rgba32);
if (rgba32.A == 0)
Span<TPixel> span = accessor.GetRowSpan(y);
for (int x = 0; x < accessor.Width; x++)
{
span[x].FromRgba32(Color.Transparent);
span[x].ToRgba32(ref rgba32);
if (rgba32.A == 0)
{
span[x].FromRgba32(Color.Transparent);
}
}
}
}
});
}
/// <summary>
@ -914,27 +918,31 @@ namespace SixLabors.ImageSharp.Formats.Png
using IMemoryOwner<byte> filterBuffer = this.memoryAllocator.Allocate<byte>(filterLength, AllocationOptions.Clean);
using IMemoryOwner<byte> attemptBuffer = this.memoryAllocator.Allocate<byte>(filterLength, AllocationOptions.Clean);
Span<byte> filter = filterBuffer.GetSpan();
Span<byte> attempt = attemptBuffer.GetSpan();
for (int y = 0; y < this.height; y++)
pixels.ProcessPixelRows(accessor =>
{
this.CollectAndFilterPixelRow(pixels.DangerousGetRowSpan(y), ref filter, ref attempt, quantized, y);
deflateStream.Write(filter);
this.SwapScanlineBuffers();
}
Span<byte> filter = filterBuffer.GetSpan();
Span<byte> attempt = attemptBuffer.GetSpan();
for (int y = 0; y < this.height; y++)
{
this.CollectAndFilterPixelRow(accessor.GetRowSpan(y), ref filter, ref attempt, quantized, y);
deflateStream.Write(filter);
this.SwapScanlineBuffers();
}
});
}
/// <summary>
/// Interlaced encoding the pixels.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="pixels">The pixels.</param>
/// <param name="image">The image.</param>
/// <param name="deflateStream">The deflate stream.</param>
private void EncodeAdam7Pixels<TPixel>(Image<TPixel> pixels, ZlibDeflateStream deflateStream)
private void EncodeAdam7Pixels<TPixel>(Image<TPixel> image, ZlibDeflateStream deflateStream)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = pixels.Width;
int height = pixels.Height;
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> pixelBuffer = image.Frames.RootFrame.PixelBuffer;
for (int pass = 0; pass < 7; pass++)
{
int startRow = Adam7.FirstRow[pass];
@ -959,7 +967,7 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int row = startRow; row < height; row += Adam7.RowIncrement[pass])
{
// Collect pixel data
Span<TPixel> srcRow = pixels.DangerousGetRowSpan(row);
Span<TPixel> srcRow = pixelBuffer.DangerousGetRowSpan(row);
for (int col = startCol, i = 0; col < width; col += Adam7.ColumnIncrement[pass])
{
block[i++] = srcRow[col];

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

@ -42,18 +42,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
// Special case for T4BitCompressor.
int stripPixels = width * height;
this.pixelsAsGray ??= this.MemoryAllocator.Allocate<byte>(stripPixels);
Span<byte> pixelAsGraySpan = this.pixelsAsGray.GetSpan();
int lastRow = y + height;
int grayRowIdx = 0;
for (int row = y; row < lastRow; row++)
this.imageBlackWhite.ProcessPixelRows(accessor =>
{
Span<TPixel> pixelsBlackWhiteRow = this.imageBlackWhite.DangerousGetRowSpan(row);
Span<byte> pixelAsGrayRow = pixelAsGraySpan.Slice(grayRowIdx * width, width);
PixelOperations<TPixel>.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGrayRow, width);
grayRowIdx++;
}
Span<byte> pixelAsGraySpan = this.pixelsAsGray.GetSpan();
int lastRow = y + height;
int grayRowIdx = 0;
for (int row = y; row < lastRow; row++)
{
Span<TPixel> pixelsBlackWhiteRow = accessor.GetRowSpan(row);
Span<byte> pixelAsGrayRow = pixelAsGraySpan.Slice(grayRowIdx * width, width);
PixelOperations<TPixel>.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGrayRow, width);
grayRowIdx++;
}
compressor.CompressStrip(pixelAsGraySpan.Slice(0, stripPixels), height);
compressor.CompressStrip(pixelAsGraySpan.Slice(0, stripPixels), height);
});
}
else
{
@ -65,6 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
Span<byte> rows = this.bitStrip.Slice(0, bytesPerStrip);
rows.Clear();
Buffer2D<TPixel> blackWhiteBuffer = this.imageBlackWhite.Frames.RootFrame.PixelBuffer;
int outputRowIdx = 0;
int lastRow = y + height;
@ -73,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
int bitIndex = 0;
int byteIndex = 0;
Span<byte> outputRow = rows.Slice(outputRowIdx * this.BytesPerRow);
Span<TPixel> pixelsBlackWhiteRow = this.imageBlackWhite.DangerousGetRowSpan(row);
Span<TPixel> pixelsBlackWhiteRow = blackWhiteBuffer.DangerousGetRowSpan(row);
PixelOperations<TPixel>.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGraySpan, width);
for (int x = 0; x < this.Image.Width; x++)
{

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

@ -389,13 +389,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private bool ConvertPixelsToBgra<TPixel>(Image<TPixel> image, int width, int height)
where TPixel : unmanaged, IPixel<TPixel>
{
Buffer2D<TPixel> imageBuffer = image.Frames.RootFrame.PixelBuffer;
bool nonOpaque = false;
Span<uint> bgra = this.Bgra.GetSpan();
Span<byte> bgraBytes = MemoryMarshal.Cast<uint, byte>(bgra);
int widthBytes = width * 4;
for (int y = 0; y < height; y++)
{
Span<TPixel> rowSpan = image.DangerousGetRowSpan(y);
Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(y);
Span<byte> rowBytes = bgraBytes.Slice(y * widthBytes, widthBytes);
PixelOperations<TPixel>.Instance.ToBgra32Bytes(this.configuration, rowSpan, rowBytes, width);
if (!nonOpaque)

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

@ -31,8 +31,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void ConvertRgbToYuv<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator, Span<byte> y, Span<byte> u, Span<byte> v)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
Buffer2D<TPixel> imageBuffer = image.Frames.RootFrame.PixelBuffer;
int width = imageBuffer.Width;
int height = imageBuffer.Height;
int uvWidth = (width + 1) >> 1;
// Temporary storage for accumulated R/G/B values during conversion to U/V.
@ -46,8 +47,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int rowIndex;
for (rowIndex = 0; rowIndex < height - 1; rowIndex += 2)
{
Span<TPixel> rowSpan = image.DangerousGetRowSpan(rowIndex);
Span<TPixel> nextRowSpan = image.DangerousGetRowSpan(rowIndex + 1);
Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(rowIndex);
Span<TPixel> nextRowSpan = imageBuffer.DangerousGetRowSpan(rowIndex + 1);
PixelOperations<TPixel>.Instance.ToBgra32(configuration, rowSpan, bgraRow0);
PixelOperations<TPixel>.Instance.ToBgra32(configuration, nextRowSpan, bgraRow1);
@ -73,7 +74,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Extra last row.
if ((height & 1) != 0)
{
Span<TPixel> rowSpan = image.DangerousGetRowSpan(rowIndex);
Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(rowIndex);
PixelOperations<TPixel>.Instance.ToBgra32(configuration, rowSpan, bgraRow0);
ConvertRgbaToY(bgraRow0, y.Slice(rowIndex * width), width);

20
src/ImageSharp/Image{TPixel}.cs

@ -300,26 +300,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> DangerousGetRowSpan(int rowIndex)
{
Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex));
Guard.MustBeLessThan(rowIndex, this.Height, nameof(rowIndex));
this.EnsureNotDisposed();
return this.PixelSourceUnsafe.PixelBuffer.DangerousGetRowSpan(rowIndex);
}
/// <summary>
/// Gets the representation of the pixels as a <see cref="Memory{T}"/> in the source image's pixel format
/// stored in row major order, if the backing buffer is contiguous.

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

@ -30,11 +30,12 @@ namespace SixLabors.ImageSharp.Processing
Buffer2D<ulong> intImage = configuration.MemoryAllocator.Allocate2D<ulong>(source.Width, source.Height);
ulong sumX0 = 0;
Buffer2D<TPixel> sourceBuffer = source.Frames.RootFrame.PixelBuffer;
using (IMemoryOwner<L8> tempRow = configuration.MemoryAllocator.Allocate<L8>(source.Width))
{
Span<L8> tempSpan = tempRow.GetSpan();
Span<TPixel> sourceRow = source.DangerousGetRowSpan(0);
Span<TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(0);
Span<ulong> destRow = intImage.DangerousGetRowSpan(0);
PixelOperations<TPixel>.Instance.ToL8(configuration, sourceRow, tempSpan);
@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing
// All other rows
for (int y = 1; y < endY; y++)
{
sourceRow = source.DangerousGetRowSpan(y);
sourceRow = sourceBuffer.DangerousGetRowSpan(y);
destRow = intImage.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToL8(configuration, sourceRow, tempSpan);

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

@ -148,22 +148,5 @@ namespace SixLabors.ImageSharp.Tests.Advanced
Assert.ThrowsAny<InvalidMemoryOperationException>(() => _ = memory3.Span);
Assert.ThrowsAny<InvalidMemoryOperationException>(() => _ = memory10.Span);
}
[Theory]
[WithBlankImages(1, 1, PixelTypes.Rgba32)]
[WithBlankImages(100, 111, PixelTypes.Rgba32)]
[WithBlankImages(400, 600, PixelTypes.Rgba32)]
public void DangerousGetRowSpan_ShouldReferenceSpanOfMemory<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(200);
using Image<TPixel> image = provider.GetImage();
Memory<TPixel> memory = image.DangerousGetPixelRowMemory(image.Height - 1);
Span<TPixel> span = image.DangerousGetRowSpan(image.Height - 1);
Assert.True(span == memory.Span);
}
}
}

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

@ -411,21 +411,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
ColorType = colorType
};
Rgba32 rgba32 = Color.Blue;
for (int y = 0; y < image.Height; y++)
image.ProcessPixelRows(accessor =>
{
System.Span<Rgba32> rowSpan = image.DangerousGetRowSpan(y);
// Half of the test image should be transparent.
if (y > 25)
for (int y = 0; y < image.Height; y++)
{
rgba32.A = 0;
}
System.Span<Rgba32> rowSpan = accessor.GetRowSpan(y);
for (int x = 0; x < image.Width; x++)
{
rowSpan[x].FromRgba32(rgba32);
// Half of the test image should be transparent.
if (y > 25)
{
rgba32.A = 0;
}
for (int x = 0; x < image.Width; x++)
{
rowSpan[x].FromRgba32(rgba32);
}
}
}
});
// act
using var memStream = new MemoryStream();
@ -441,20 +444,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
expectedColor = new Rgba32(luminance, luminance, luminance);
}
for (int y = 0; y < actual.Height; y++)
actual.ProcessPixelRows(accessor =>
{
System.Span<Rgba32> rowSpan = actual.DangerousGetRowSpan(y);
if (y > 25)
for (int y = 0; y < accessor.Height; y++)
{
expectedColor = Color.Transparent;
}
System.Span<Rgba32> rowSpan = accessor.GetRowSpan(y);
for (int x = 0; x < actual.Width; x++)
{
Assert.Equal(expectedColor, rowSpan[x]);
if (y > 25)
{
expectedColor = Color.Transparent;
}
for (int x = 0; x < accessor.Width; x++)
{
Assert.Equal(expectedColor, rowSpan[x]);
}
}
}
});
}
[Theory]

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

@ -131,15 +131,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
where TPixel : unmanaged, IPixel<TPixel>
{
uint[] bgra = new uint[image.Width * image.Height];
int idx = 0;
for (int y = 0; y < image.Height; y++)
image.ProcessPixelRows(accessor =>
{
Span<TPixel> rowSpan = image.DangerousGetRowSpan(y);
for (int x = 0; x < rowSpan.Length; x++)
int idx = 0;
for (int y = 0; y < accessor.Height; y++)
{
bgra[idx++] = ToBgra32(rowSpan[x]).PackedValue;
Span<TPixel> rowSpan = accessor.GetRowSpan(y);
for (int x = 0; x < rowSpan.Length; x++)
{
bgra[idx++] = ToBgra32(rowSpan[x]).PackedValue;
}
}
}
});
return bgra;
}

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

@ -32,15 +32,17 @@ namespace SixLabors.ImageSharp.Tests
[WithTestPatternImages(9, 9, PixelTypes.Rgba32)]
public void CloneAs_ToBgra32(TestImageProvider<Rgba32> provider)
{
using (Image<Rgba32> image = provider.GetImage())
using (Image<Bgra32> clone = image.CloneAs<Bgra32>())
using Image<Rgba32> image = provider.GetImage();
using Image<Bgra32> clone = image.CloneAs<Bgra32>();
image.ProcessPixelRows(clone, static (imageAccessor, cloneAccessor) =>
{
for (int y = 0; y < image.Height; y++)
for (int y = 0; y < imageAccessor.Height; y++)
{
Span<Rgba32> row = image.DangerousGetRowSpan(y);
Span<Bgra32> rowClone = clone.DangerousGetRowSpan(y);
Span<Rgba32> row = imageAccessor.GetRowSpan(y);
Span<Bgra32> rowClone = cloneAccessor.GetRowSpan(y);
for (int x = 0; x < image.Width; x++)
for (int x = 0; x < imageAccessor.Width; x++)
{
Rgba32 expected = row[x];
Bgra32 actual = rowClone[x];
@ -51,22 +53,24 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(expected.A, actual.A);
}
}
}
});
}
[Theory]
[WithTestPatternImages(9, 9, PixelTypes.Rgba32)]
public void CloneAs_ToBgr24(TestImageProvider<Rgba32> provider)
{
using (Image<Rgba32> image = provider.GetImage())
using (Image<Bgr24> clone = image.CloneAs<Bgr24>())
using Image<Rgba32> image = provider.GetImage();
using Image<Bgr24> clone = image.CloneAs<Bgr24>();
image.ProcessPixelRows(clone, static (imageAccessor, cloneAccessor) =>
{
for (int y = 0; y < image.Height; y++)
for (int y = 0; y < imageAccessor.Height; y++)
{
Span<Rgba32> row = image.DangerousGetRowSpan(y);
Span<Bgr24> rowClone = clone.DangerousGetRowSpan(y);
Span<Rgba32> row = imageAccessor.GetRowSpan(y);
Span<Bgr24> rowClone = cloneAccessor.GetRowSpan(y);
for (int x = 0; x < image.Width; x++)
for (int x = 0; x < cloneAccessor.Width; x++)
{
Rgba32 expected = row[x];
Bgr24 actual = rowClone[x];
@ -76,22 +80,23 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(expected.B, actual.B);
}
}
}
});
}
[Theory]
[WithTestPatternImages(9, 9, PixelTypes.Rgba32)]
public void CloneAs_ToArgb32(TestImageProvider<Rgba32> provider)
{
using (Image<Rgba32> image = provider.GetImage())
using (Image<Argb32> clone = image.CloneAs<Argb32>())
using Image<Rgba32> image = provider.GetImage();
using Image<Argb32> clone = image.CloneAs<Argb32>();
image.ProcessPixelRows(clone, static (imageAccessor, cloneAccessor) =>
{
for (int y = 0; y < image.Height; y++)
for (int y = 0; y < imageAccessor.Height; y++)
{
Span<Rgba32> row = image.DangerousGetRowSpan(y);
Span<Argb32> rowClone = clone.DangerousGetRowSpan(y);
Span<Rgba32> row = imageAccessor.GetRowSpan(y);
Span<Argb32> rowClone = cloneAccessor.GetRowSpan(y);
for (int x = 0; x < image.Width; x++)
for (int x = 0; x < cloneAccessor.Width; x++)
{
Rgba32 expected = row[x];
Argb32 actual = rowClone[x];
@ -102,22 +107,23 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(expected.A, actual.A);
}
}
}
});
}
[Theory]
[WithTestPatternImages(9, 9, PixelTypes.Rgba32)]
public void CloneAs_ToRgb24(TestImageProvider<Rgba32> provider)
{
using (Image<Rgba32> image = provider.GetImage())
using (Image<Rgb24> clone = image.CloneAs<Rgb24>())
using Image<Rgba32> image = provider.GetImage();
using Image<Rgb24> clone = image.CloneAs<Rgb24>();
image.ProcessPixelRows(clone, static (imageAccessor, cloneAccessor) =>
{
for (int y = 0; y < image.Height; y++)
for (int y = 0; y < imageAccessor.Height; y++)
{
Span<Rgba32> row = image.DangerousGetRowSpan(y);
Span<Rgb24> rowClone = clone.DangerousGetRowSpan(y);
Span<Rgba32> row = imageAccessor.GetRowSpan(y);
Span<Rgb24> rowClone = cloneAccessor.GetRowSpan(y);
for (int x = 0; x < image.Width; x++)
for (int x = 0; x < imageAccessor.Width; x++)
{
Rgba32 expected = row[x];
Rgb24 actual = rowClone[x];
@ -127,7 +133,8 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(expected.B, actual.B);
}
}
}
});
}
}
}

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

@ -163,10 +163,14 @@ namespace SixLabors.ImageSharp.Tests
{
Assert.Equal(memory, image.GetRootFramePixelBuffer().DangerousGetSingleMemory());
image.GetPixelMemoryGroup().Fill(bg);
for (var i = 10; i < 20; i++)
image.ProcessPixelRows(accessor =>
{
image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg);
}
for (var i = 10; i < 20; i++)
{
accessor.GetRowSpan(i).Slice(10, 10).Fill(fg);
}
});
}
Assert.False(memoryManager.IsDisposed);
@ -198,10 +202,13 @@ namespace SixLabors.ImageSharp.Tests
{
Assert.Equal(memoryManager.Memory, image.GetRootFramePixelBuffer().DangerousGetSingleMemory());
image.GetPixelMemoryGroup().Fill(bg);
for (var i = 10; i < 20; i++)
image.ProcessPixelRows(accessor =>
{
image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg);
}
for (var i = 10; i < 20; i++)
{
accessor.GetRowSpan(i).Slice(10, 10).Fill(fg);
}
});
}
Assert.True(memoryManager.IsDisposed);
@ -263,10 +270,13 @@ namespace SixLabors.ImageSharp.Tests
Assert.True(Unsafe.AreSame(ref pixelSpan.GetPinnableReference(), ref imageSpan.GetPinnableReference()));
image.GetPixelMemoryGroup().Fill(bg);
for (var i = 10; i < 20; i++)
image.ProcessPixelRows(accessor =>
{
image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg);
}
for (var i = 10; i < 20; i++)
{
accessor.GetRowSpan(i).Slice(10, 10).Fill(fg);
}
});
}
Assert.False(memoryManager.IsDisposed);
@ -332,10 +342,13 @@ namespace SixLabors.ImageSharp.Tests
Assert.True(Unsafe.AreSame(ref pixelSpan.GetPinnableReference(), ref imageSpan.GetPinnableReference()));
image.GetPixelMemoryGroup().Fill(bg);
for (var i = 10; i < 20; i++)
image.ProcessPixelRows(accessor =>
{
image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg);
}
for (var i = 10; i < 20; i++)
{
accessor.GetRowSpan(i).Slice(10, 10).Fill(fg);
}
});
}
Assert.False(memoryManager.IsDisposed);
@ -413,16 +426,19 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(width, img.Width);
Assert.Equal(height, img.Height);
for (int i = 0; i < height; ++i)
img.ProcessPixelRows(accessor =>
{
var arrayIndex = width * i;
for (int i = 0; i < height; ++i)
{
var arrayIndex = width * i;
Span<Rgba32> rowSpan = img.DangerousGetRowSpan(i);
ref Rgba32 r0 = ref rowSpan[0];
ref Rgba32 r1 = ref array[arrayIndex];
Span<Rgba32> rowSpan = accessor.GetRowSpan(i);
ref Rgba32 r0 = ref rowSpan[0];
ref Rgba32 r1 = ref array[arrayIndex];
Assert.True(Unsafe.AreSame(ref r0, ref r1));
}
Assert.True(Unsafe.AreSame(ref r0, ref r1));
}
});
}
Assert.True(memory.Disposed);
@ -457,16 +473,19 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(width, img.Width);
Assert.Equal(height, img.Height);
for (int i = 0; i < height; ++i)
img.ProcessPixelRows(acccessor =>
{
var arrayIndex = pixelSize * width * i;
for (int i = 0; i < height; ++i)
{
var arrayIndex = pixelSize * width * i;
Span<Rgba32> rowSpan = img.DangerousGetRowSpan(i);
ref Rgba32 r0 = ref rowSpan[0];
ref Rgba32 r1 = ref Unsafe.As<byte, Rgba32>(ref array[arrayIndex]);
Span<Rgba32> rowSpan = acccessor.GetRowSpan(i);
ref Rgba32 r0 = ref rowSpan[0];
ref Rgba32 r1 = ref Unsafe.As<byte, Rgba32>(ref array[arrayIndex]);
Assert.True(Unsafe.AreSame(ref r0, ref r1));
}
Assert.True(Unsafe.AreSame(ref r0, ref r1));
}
});
}
Assert.True(memory.Disposed);

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

@ -271,7 +271,6 @@ namespace SixLabors.ImageSharp.Tests
// Image<TPixel>
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.DangerousGetRowSpan(default); });
Assert.Throws<ObjectDisposedException>(() => { var res = image.DangerousTryGetSinglePixelMemory(out Memory<Rgba32> _); });
// Image

17
tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs

@ -154,6 +154,23 @@ namespace SixLabors.ImageSharp.Tests
}
}
[Fact]
public void Disposed_ThrowsObjectDisposedException()
{
using var nonDisposed = new Image<L16>(1, 1);
var disposed = new Image<L16>(1, 1);
disposed.Dispose();
Assert.Throws<ObjectDisposedException>(() => this.ProcessPixelRowsImpl(disposed, _ => { }));
Assert.Throws<ObjectDisposedException>(() => this.ProcessPixelRowsImpl(disposed, nonDisposed, (_, _) => { }));
Assert.Throws<ObjectDisposedException>(() => this.ProcessPixelRowsImpl(nonDisposed, disposed, (_, _) => { }));
Assert.Throws<ObjectDisposedException>(() => this.ProcessPixelRowsImpl(disposed, nonDisposed, nonDisposed, (_, _, _) => { }));
Assert.Throws<ObjectDisposedException>(() => this.ProcessPixelRowsImpl(nonDisposed, disposed, nonDisposed, (_, _, _) => { }));
Assert.Throws<ObjectDisposedException>(() => this.ProcessPixelRowsImpl(nonDisposed, nonDisposed, disposed, (_, _, _) => { }));
}
[Fact]
public void RetainsUnmangedBuffers1()
{

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

@ -93,25 +93,31 @@ namespace SixLabors.ImageSharp.Tests.Quantization
Assert.Equal(1, result.Width);
Assert.Equal(256, result.Height);
var actualImage = new Image<Rgba32>(1, 256);
using var actualImage = new Image<Rgba32>(1, 256);
ReadOnlySpan<Rgba32> paletteSpan = result.Palette.Span;
int paletteCount = paletteSpan.Length - 1;
for (int y = 0; y < actualImage.Height; y++)
actualImage.ProcessPixelRows(accessor =>
{
Span<Rgba32> row = actualImage.DangerousGetRowSpan(y);
ReadOnlySpan<byte> quantizedPixelSpan = result.DangerousGetRowSpan(y);
for (int x = 0; x < actualImage.Width; x++)
ReadOnlySpan<Rgba32> paletteSpan = result.Palette.Span;
int paletteCount = paletteSpan.Length - 1;
for (int y = 0; y < accessor.Height; y++)
{
row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])];
Span<Rgba32> row = accessor.GetRowSpan(y);
ReadOnlySpan<byte> quantizedPixelSpan = result.DangerousGetRowSpan(y);
for (int x = 0; x < accessor.Width; x++)
{
row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])];
}
}
}
});
for (int y = 0; y < image.Height; y++)
image.ProcessPixelRows(actualImage, static (imageAccessor, actualImageAccessor) =>
{
Assert.True(image.DangerousGetRowSpan(y).SequenceEqual(actualImage.DangerousGetRowSpan(y)));
}
for (int y = 0; y < imageAccessor.Height; y++)
{
Assert.True(imageAccessor.GetRowSpan(y).SequenceEqual(actualImageAccessor.GetRowSpan(y)));
}
});
}
[Theory]
@ -162,24 +168,30 @@ namespace SixLabors.ImageSharp.Tests.Quantization
Assert.Equal(1, result.Width);
Assert.Equal(256, result.Height);
ReadOnlySpan<Rgba32> paletteSpan = result.Palette.Span;
int paletteCount = paletteSpan.Length - 1;
for (int y = 0; y < actualImage.Height; y++)
actualImage.ProcessPixelRows(accessor =>
{
Span<Rgba32> row = actualImage.DangerousGetRowSpan(y);
ReadOnlySpan<byte> quantizedPixelSpan = result.DangerousGetRowSpan(y);
for (int x = 0; x < actualImage.Width; x++)
ReadOnlySpan<Rgba32> paletteSpan = result.Palette.Span;
int paletteCount = paletteSpan.Length - 1;
for (int y = 0; y < accessor.Height; y++)
{
row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])];
Span<Rgba32> row = accessor.GetRowSpan(y);
ReadOnlySpan<byte> quantizedPixelSpan = result.DangerousGetRowSpan(y);
for (int x = 0; x < accessor.Width; x++)
{
row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])];
}
}
}
});
}
for (int y = 0; y < expectedImage.Height; y++)
expectedImage.ProcessPixelRows(actualImage, static (expectedAccessor, actualAccessor) =>
{
Assert.True(expectedImage.DangerousGetRowSpan(y).SequenceEqual(actualImage.DangerousGetRowSpan(y)));
}
for (int y = 0; y < expectedAccessor.Height; y++)
{
Assert.True(expectedAccessor.GetRowSpan(y).SequenceEqual(actualAccessor.GetRowSpan(y)));
}
});
}
}
}

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

@ -39,25 +39,29 @@ namespace SixLabors.ImageSharp.Tests
public override Image<TPixel> GetImage()
{
var result = new Image<TPixel>(this.Configuration, this.Width, this.Height);
result.ProcessPixelRows(accessor =>
{
int midY = this.Height / 2;
int midX = this.Width / 2;
int midY = this.Height / 2;
int midX = this.Width / 2;
for (int y = 0; y < midY; y++)
{
Span<TPixel> row = accessor.GetRowSpan(y);
for (int y = 0; y < midY; y++)
{
Span<TPixel> row = result.DangerousGetRowSpan(y);
row.Slice(0, midX).Fill(TopLeftColor);
row.Slice(midX, this.Width - midX).Fill(TopRightColor);
}
row.Slice(0, midX).Fill(TopLeftColor);
row.Slice(midX, this.Width - midX).Fill(TopRightColor);
}
for (int y = midY; y < this.Height; y++)
{
Span<TPixel> row = accessor.GetRowSpan(y);
row.Slice(0, midX).Fill(BottomLeftColor);
row.Slice(midX, this.Width - midX).Fill(BottomRightColor);
}
});
for (int y = midY; y < this.Height; y++)
{
Span<TPixel> row = result.DangerousGetRowSpan(y);
row.Slice(0, midX).Fill(BottomLeftColor);
row.Slice(midX, this.Width - midX).Fill(BottomRightColor);
}
return result;
}

Loading…
Cancel
Save