mirror of https://github.com/SixLabors/ImageSharp
10 changed files with 549 additions and 122 deletions
@ -0,0 +1,260 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using System.Collections.Generic; |
|||
using Microsoft.DotNet.RemoteExecutor; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.Memory.Internals; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using Xunit; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests |
|||
{ |
|||
public abstract class ProcessPixelRowsTestBase |
|||
{ |
|||
protected abstract void ProcessPixelRowsImpl<TPixel>( |
|||
Image<TPixel> image, |
|||
PixelAccessorAction<TPixel> processPixels) |
|||
where TPixel : unmanaged, IPixel<TPixel>; |
|||
|
|||
protected abstract void ProcessPixelRowsImpl<TPixel>( |
|||
Image<TPixel> image1, |
|||
Image<TPixel> image2, |
|||
PixelAccessorAction<TPixel, TPixel> processPixels) |
|||
where TPixel : unmanaged, IPixel<TPixel>; |
|||
|
|||
protected abstract void ProcessPixelRowsImpl<TPixel>( |
|||
Image<TPixel> image1, |
|||
Image<TPixel> image2, |
|||
Image<TPixel> image3, |
|||
PixelAccessorAction<TPixel, TPixel, TPixel> processPixels) |
|||
where TPixel : unmanaged, IPixel<TPixel>; |
|||
|
|||
[Fact] |
|||
public void PixelAccessorDimensionsAreCorrect() |
|||
{ |
|||
using var image = new Image<Rgb24>(123, 456); |
|||
this.ProcessPixelRowsImpl(image, accessor => |
|||
{ |
|||
Assert.Equal(123, accessor.Width); |
|||
Assert.Equal(456, accessor.Height); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void WriteImagePixels_SingleImage() |
|||
{ |
|||
using var image = new Image<L16>(256, 256); |
|||
this.ProcessPixelRowsImpl(image, accessor => |
|||
{ |
|||
for (int y = 0; y < accessor.Height; y++) |
|||
{ |
|||
Span<L16> row = accessor.GetRowSpan(y); |
|||
for (int x = 0; x < row.Length; x++) |
|||
{ |
|||
row[x] = new L16((ushort)(x * y)); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
Buffer2D<L16> buffer = image.Frames.RootFrame.PixelBuffer; |
|||
for (int y = 0; y < 256; y++) |
|||
{ |
|||
Span<L16> row = buffer.GetRowSpan(y); |
|||
for (int x = 0; x < 256; x++) |
|||
{ |
|||
int actual = row[x].PackedValue; |
|||
Assert.Equal(x * y, actual); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void WriteImagePixels_MultiImage2() |
|||
{ |
|||
using var img1 = new Image<L16>(256, 256); |
|||
Buffer2D<L16> buffer = img1.Frames.RootFrame.PixelBuffer; |
|||
for (int y = 0; y < 256; y++) |
|||
{ |
|||
Span<L16> row = buffer.GetRowSpan(y); |
|||
for (int x = 0; x < 256; x++) |
|||
{ |
|||
row[x] = new L16((ushort)(x * y)); |
|||
} |
|||
} |
|||
|
|||
using var img2 = new Image<L16>(256, 256); |
|||
|
|||
this.ProcessPixelRowsImpl(img1, img2, (accessor1, accessor2) => |
|||
{ |
|||
for (int y = 0; y < accessor1.Height; y++) |
|||
{ |
|||
Span<L16> row1 = accessor1.GetRowSpan(y); |
|||
Span<L16> row2 = accessor2.GetRowSpan(accessor2.Height - y - 1); |
|||
row1.CopyTo(row2); |
|||
} |
|||
}); |
|||
|
|||
buffer = img2.Frames.RootFrame.PixelBuffer; |
|||
for (int y = 0; y < 256; y++) |
|||
{ |
|||
Span<L16> row = buffer.GetRowSpan(y); |
|||
for (int x = 0; x < 256; x++) |
|||
{ |
|||
int actual = row[x].PackedValue; |
|||
Assert.Equal(x * (256 - y - 1), actual); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void WriteImagePixels_MultiImage3() |
|||
{ |
|||
using var img1 = new Image<L16>(256, 256); |
|||
Buffer2D<L16> buffer2 = img1.Frames.RootFrame.PixelBuffer; |
|||
for (int y = 0; y < 256; y++) |
|||
{ |
|||
Span<L16> row = buffer2.GetRowSpan(y); |
|||
for (int x = 0; x < 256; x++) |
|||
{ |
|||
row[x] = new L16((ushort)(x * y)); |
|||
} |
|||
} |
|||
|
|||
using var img2 = new Image<L16>(256, 256); |
|||
using var img3 = new Image<L16>(256, 256); |
|||
|
|||
this.ProcessPixelRowsImpl(img1, img2, img3, (accessor1, accessor2, accessor3) => |
|||
{ |
|||
for (int y = 0; y < accessor1.Height; y++) |
|||
{ |
|||
Span<L16> row1 = accessor1.GetRowSpan(y); |
|||
Span<L16> row2 = accessor2.GetRowSpan(accessor2.Height - y - 1); |
|||
Span<L16> row3 = accessor3.GetRowSpan(y); |
|||
row1.CopyTo(row2); |
|||
row1.CopyTo(row3); |
|||
} |
|||
}); |
|||
|
|||
buffer2 = img2.Frames.RootFrame.PixelBuffer; |
|||
Buffer2D<L16> buffer3 = img3.Frames.RootFrame.PixelBuffer; |
|||
for (int y = 0; y < 256; y++) |
|||
{ |
|||
Span<L16> row2 = buffer2.GetRowSpan(y); |
|||
Span<L16> row3 = buffer3.GetRowSpan(y); |
|||
for (int x = 0; x < 256; x++) |
|||
{ |
|||
int actual2 = row2[x].PackedValue; |
|||
int actual3 = row3[x].PackedValue; |
|||
Assert.Equal(x * (256 - y - 1), actual2); |
|||
Assert.Equal(x * y, actual3); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void RetainsUnmangedBuffers1() |
|||
{ |
|||
RemoteExecutor.Invoke(RunTest, this.GetType().FullName).Dispose(); |
|||
|
|||
static void RunTest(string testTypeName) |
|||
{ |
|||
var buffer = new UnmanagedBuffer<L8>(100); |
|||
var allocator = new MockUnmanagedMemoryAllocator<L8>(buffer); |
|||
Configuration.Default.MemoryAllocator = allocator; |
|||
|
|||
var image = new Image<L8>(10, 10); |
|||
|
|||
Assert.Equal(1, UnmanagedMemoryHandle.TotalOutstandingHandles); |
|||
GetTest(testTypeName).ProcessPixelRowsImpl(image, _ => |
|||
{ |
|||
buffer.BufferHandle.Dispose(); |
|||
Assert.Equal(1, UnmanagedMemoryHandle.TotalOutstandingHandles); |
|||
}); |
|||
Assert.Equal(0, UnmanagedMemoryHandle.TotalOutstandingHandles); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void RetainsUnmangedBuffers2() |
|||
{ |
|||
RemoteExecutor.Invoke(RunTest, this.GetType().FullName).Dispose(); |
|||
|
|||
static void RunTest(string testTypeName) |
|||
{ |
|||
var buffer1 = new UnmanagedBuffer<L8>(100); |
|||
var buffer2 = new UnmanagedBuffer<L8>(100); |
|||
var allocator = new MockUnmanagedMemoryAllocator<L8>(buffer1, buffer2); |
|||
Configuration.Default.MemoryAllocator = allocator; |
|||
|
|||
var image1 = new Image<L8>(10, 10); |
|||
var image2 = new Image<L8>(10, 10); |
|||
|
|||
Assert.Equal(2, UnmanagedMemoryHandle.TotalOutstandingHandles); |
|||
GetTest(testTypeName).ProcessPixelRowsImpl(image1, image2, (_, _) => |
|||
{ |
|||
buffer1.BufferHandle.Dispose(); |
|||
buffer2.BufferHandle.Dispose(); |
|||
Assert.Equal(2, UnmanagedMemoryHandle.TotalOutstandingHandles); |
|||
}); |
|||
Assert.Equal(0, UnmanagedMemoryHandle.TotalOutstandingHandles); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void RetainsUnmangedBuffers3() |
|||
{ |
|||
RemoteExecutor.Invoke(RunTest, this.GetType().FullName).Dispose(); |
|||
|
|||
static void RunTest(string testTypeName) |
|||
{ |
|||
var buffer1 = new UnmanagedBuffer<L8>(100); |
|||
var buffer2 = new UnmanagedBuffer<L8>(100); |
|||
var buffer3 = new UnmanagedBuffer<L8>(100); |
|||
var allocator = new MockUnmanagedMemoryAllocator<L8>(buffer1, buffer2, buffer3); |
|||
Configuration.Default.MemoryAllocator = allocator; |
|||
|
|||
var image1 = new Image<L8>(10, 10); |
|||
var image2 = new Image<L8>(10, 10); |
|||
var image3 = new Image<L8>(10, 10); |
|||
|
|||
Assert.Equal(3, UnmanagedMemoryHandle.TotalOutstandingHandles); |
|||
GetTest(testTypeName).ProcessPixelRowsImpl(image1, image2, image3, (_, _, _) => |
|||
{ |
|||
buffer1.BufferHandle.Dispose(); |
|||
buffer2.BufferHandle.Dispose(); |
|||
buffer3.BufferHandle.Dispose(); |
|||
Assert.Equal(3, UnmanagedMemoryHandle.TotalOutstandingHandles); |
|||
}); |
|||
Assert.Equal(0, UnmanagedMemoryHandle.TotalOutstandingHandles); |
|||
} |
|||
} |
|||
|
|||
private static ProcessPixelRowsTestBase GetTest(string testTypeName) |
|||
{ |
|||
Type type = typeof(ProcessPixelRowsTestBase).Assembly.GetType(testTypeName); |
|||
return (ProcessPixelRowsTestBase)Activator.CreateInstance(type); |
|||
} |
|||
|
|||
private class MockUnmanagedMemoryAllocator<T1> : MemoryAllocator |
|||
where T1 : struct |
|||
{ |
|||
private Stack<UnmanagedBuffer<T1>> buffers = new(); |
|||
|
|||
public MockUnmanagedMemoryAllocator(params UnmanagedBuffer<T1>[] buffers) |
|||
{ |
|||
foreach (UnmanagedBuffer<T1> buffer in buffers) |
|||
{ |
|||
this.buffers.Push(buffer); |
|||
} |
|||
} |
|||
|
|||
protected internal override int GetBufferCapacityInBytes() => int.MaxValue; |
|||
|
|||
public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None) => |
|||
(IMemoryOwner<T>)this.buffers.Pop(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue