@ -2,10 +2,13 @@
// Licensed under the Apache License, Version 2.0.
using System ;
using System.Linq ;
using System.Runtime.CompilerServices ;
using SixLabors.ImageSharp.Advanced ;
using SixLabors.ImageSharp.Memory ;
using SixLabors.ImageSharp.PixelFormats ;
using SixLabors.ImageSharp.Processing ;
using SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers ;
using Xunit ;
// ReSharper disable InconsistentNaming
@ -13,113 +16,145 @@ namespace SixLabors.ImageSharp.Tests.Advanced
{
public class AdvancedImageExtensionsTests
{
public class GetPixelMemory
public class GetPixelMemoryGroup
{
[Theory]
[WithSolidFilledImages(1, 1, "Red", PixelTypes.Rgba32)]
[WithTestPatternImages(131, 127, PixelTypes.Rgba32 | PixelTypes.Bgr24)]
public void WhenMemoryIsOwned < TPixel > ( TestImageProvider < TPixel > provider )
[WithBasicTestPatternImages(1, 1, PixelTypes.Rgba32)]
[WithBasicTestPatternImages(131, 127, PixelTypes.Rgba32)]
[WithBasicTestPatternImages(333, 555, PixelTypes.Bgr24)]
public void OwnedMemory_PixelDataIsCorrect < TPixel > ( TestImageProvider < TPixel > provider )
where TPixel : struct , IPixel < TPixel >
{
using ( Image < TPixel > image0 = provider . GetImage ( ) )
{
var targetBuffer = new TPixel [ image0 . Width * image0 . Height ] ;
provider . LimitAllocatorBufferCapacity ( ) ;
// Act:
Memory < TPixel > memory = image0 . GetRootFramePixelBuffer ( ) . GetSingleMemory ( ) ;
using Image < TPixel > image = provider . GetImage ( ) ;
// Assert:
Assert . Equal ( image0 . Width * image0 . Height , memory . Length ) ;
memory . Span . CopyTo ( targetBuffer ) ;
// Act:
IMemoryGroup < TPixel > memoryGroup = image . GetPixelMemoryGroup ( ) ;
using ( Image < TPixel > image1 = provider . GetImage ( ) )
{
// We are using a copy of the original image for assertion
image1 . ComparePixelBufferTo ( targetBuffer ) ;
}
}
// Assert:
VerifyMemoryGroupDataMatchesTestPattern ( provider , memoryGroup , image . Size ( ) ) ;
}
[Theory]
[WithSolidFilledImages(1, 1, "Red", PixelTypes.Rgba32 | PixelTypes.Bgr24)]
[WithTestPatternImages(131, 127, PixelTypes.Rgba32 | PixelTypes.Bgr24)]
public void WhenMemoryIsConsumed < TPixel > ( TestImageProvider < TPixel > provider )
[WithBlankImages(16, 16, PixelTypes.Rgba32)]
public void OwnedMemory_DestructiveMutate_ShouldInvalidateMemoryGroup < TPixel > ( TestImageProvider < TPixel > provider )
where TPixel : struct , IPixel < TPixel >
{
using ( Image < TPixel > image0 = provider . GetImage ( ) )
{
var targetBuffer = new TPixel [ image0 . Width * image0 . Height ] ;
image0 . GetPixelSpan ( ) . CopyTo ( targetBuffer ) ;
using Image < TPixel > image = provider . GetImage ( ) ;
IMemoryGroup < TPixel > memoryGroup = image . GetPixelMemoryGroup ( ) ;
Memory < TPixel > memory = memoryGroup . Single ( ) ;
var managerOfExternalMemory = new TestMemoryManager < TPixel > ( targetBuffer ) ;
image . Mutate ( c = > c . Resize ( 8 , 8 ) ) ;
Memory < TPixel > externalMemory = managerOfExternalMemory . Memory ;
Assert . False ( memoryGroup . IsValid ) ;
Assert . ThrowsAny < InvalidMemoryOperationException > ( ( ) = > _ = memoryGroup . First ( ) ) ;
Assert . ThrowsAny < InvalidMemoryOperationException > ( ( ) = > _ = memory . Span ) ;
}
[Theory]
[WithBasicTestPatternImages(1, 1, PixelTypes.Rgba32)]
[WithBasicTestPatternImages(131, 127, PixelTypes.Bgr24)]
public void ConsumedMemory_PixelDataIsCorrect < TPixel > ( TestImageProvider < TPixel > provider )
where TPixel : struct , IPixel < TPixel >
{
using Image < TPixel > image0 = provider . GetImage ( ) ;
var targetBuffer = new TPixel [ image0 . Width * image0 . Height ] ;
image0 . GetPixelSpan ( ) . CopyTo ( targetBuffer ) ;
using ( var image1 = Image . WrapMemory ( externalMemory , image0 . Width , image0 . Height ) )
{
Memory < TPixel > internalMemory = image1 . GetRootFramePixelBuffer ( ) . GetSingleMemory ( ) ;
Assert . Equal ( targetBuffer . Length , internalMemory . Length ) ;
Assert . True ( Unsafe . AreSame ( ref targetBuffer [ 0 ] , ref internalMemory . Span [ 0 ] ) ) ;
var managerOfExternalMemory = new TestMemoryManager < TPixel > ( targetBuffer ) ;
image0 . ComparePixelBufferTo ( internalMemory . Span ) ;
}
Memory < TPixel > externalMemory = managerOfExternalMemory . Memory ;
// Make sure externalMemory works after destruction:
image0 . ComparePixelBufferTo ( externalMemory . Span ) ;
using ( var image1 = Image . WrapMemory ( externalMemory , image0 . Width , image0 . Height ) )
{
VerifyMemoryGroupDataMatchesTestPattern ( provider , image1 . GetPixelMemoryGroup ( ) , image1 . Size ( ) ) ;
}
// Make sure externalMemory works after destruction:
VerifyMemoryGroupDataMatchesTestPattern ( provider , image0 . GetPixelMemoryGroup ( ) , image0 . Size ( ) ) ;
}
}
[Theory]
[WithSolidFilledImages(1, 1, "Red", PixelTypes.Rgba32)]
[WithTestPatternImages(131, 127, PixelTypes.Rgba32 | PixelTypes.Bgr24)]
public void GetPixelRowMemory < TPixel > ( TestImageProvider < TPixel > provider )
where TPixel : struct , IPixel < TPixel >
{
using ( Image < TPixel > image = provider . GetImage ( ) )
private static void VerifyMemoryGroupDataMatchesTestPattern < TPixel > (
TestImageProvider < TPixel > provider ,
IMemoryGroup < TPixel > memoryGroup ,
Size size )
where TPixel : struct , IPixel < TPixel >
{
var targetBuffer = new TPixel [ image . Width * image . Height ] ;
Assert . True ( memoryGroup . IsValid ) ;
Assert . Equal ( size . Width * size . Height , memoryGroup . TotalLength ) ;
Assert . True ( memoryGroup . BufferLength % size . Width = = 0 ) ;
// Act:
for ( int y = 0 ; y < image . Height ; y + + )
int cnt = 0 ;
for ( MemoryGroupIndex i = memoryGroup . MaxIndex ( ) ; i < memoryGroup . MaxIndex ( ) ; i + = 1 , cnt + + )
{
Memory < TPixel > rowMemory = image . GetPixelRowMemory ( y ) ;
rowMemory . Span . CopyTo ( targetBuffer . AsSpan ( image . Width * y ) ) ;
}
int y = cnt / size . Width ;
int x = cnt % size . Width ;
// Assert:
using ( Image < TPixel > image1 = provider . GetImage ( ) )
{
// We are using a copy of the original image for assertion
image1 . ComparePixelBufferTo ( targetBuffer ) ;
TPixel expected = provider . GetExpectedBasicTestPatternPixelAt ( x , y ) ;
TPixel actual = memoryGroup . GetElementAt ( i ) ;
Assert . Equal ( expected , actual ) ;
}
}
}
[Theory]
[WithSolidFilledImages(1, 1, "Red", PixelTypes.Rgba32)]
[WithTestPatternImages(131, 127, PixelTypes.Rgba32 | PixelTypes.Bgr24)]
public void GetPixelRowSpan < TPixel > ( TestImageProvider < TPixel > provider )
[WithBasicTestPatternImages(1, 1, PixelTypes.Rgba32)]
[WithBasicTestPatternImages(131, 127, PixelTypes.Rgba32)]
[WithBasicTestPatternImages(333, 555, PixelTypes.Bgr24)]
public void GetPixelRowMemory_PixelDataIsCorrect < TPixel > ( TestImageProvider < TPixel > provider )
where TPixel : struct , IPixel < TPixel >
{
using ( Image < TPixel > image = provider . GetImage ( ) )
{
var targetBuffer = new TPixel [ image . Width * image . Height ] ;
provider . LimitAllocatorBufferCapacity ( ) ;
using Image < TPixel > image = provider . GetImage ( ) ;
for ( int y = 0 ; y < image . Height ; y + + )
{
// Act:
for ( int y = 0 ; y < image . Height ; y + + )
{
Span < TPixel > rowMemory = image . GetPixelRowSpan ( y ) ;
rowMemory . CopyTo ( targetBuffer . AsSpan ( image . Width * y ) ) ;
}
Memory < TPixel > rowMemory = image . GetPixelRowMemory ( y ) ;
Span < TPixel > span = rowMemory . Span ;
// Assert:
using ( Image < TPixel > image1 = provider . GetImage ( ) )
for ( int x = 0 ; x < image . Width ; x + + )
{
// We are using a copy of the original image for assertion
image1 . ComparePixelBufferTo ( targetBuffer ) ;
Assert . Equal ( provider . GetExpectedBasicTestPatternPixelAt ( x , y ) , span [ x ] ) ;
}
}
}
[Theory]
[WithBasicTestPatternImages(16, 16, PixelTypes.Rgba32)]
public void GetPixelRowMemory_DestructiveMutate_ShouldInvalidateMemory < TPixel > ( TestImageProvider < TPixel > provider )
where TPixel : struct , IPixel < TPixel >
{
using Image < TPixel > image = provider . GetImage ( ) ;
Memory < TPixel > memory3 = image . GetPixelRowMemory ( 3 ) ;
Memory < TPixel > memory10 = image . GetPixelRowMemory ( 1 0 ) ;
image . Mutate ( c = > c . Resize ( 8 , 8 ) ) ;
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 GetPixelRowSpan_ShouldReferenceSpanOfMemory < TPixel > ( TestImageProvider < TPixel > provider )
where TPixel : struct , IPixel < TPixel >
{
provider . LimitAllocatorBufferCapacity ( ) ;
using Image < TPixel > image = provider . GetImage ( ) ;
Memory < TPixel > memory = image . GetPixelRowMemory ( image . Height - 1 ) ;
Span < TPixel > span = image . GetPixelRowSpan ( image . Height - 1 ) ;
Assert . True ( span = = memory . Span ) ;
}
}
}