mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
36 changed files with 1891 additions and 860 deletions
@ -0,0 +1,193 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.Memory; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the resize algorithm using a sliding window of size
|
|||
/// maximized by <see cref="Configuration.WorkingBufferSizeHintInBytes"/>.
|
|||
/// The height of the window is a multiple of the vertical kernel's maximum diameter.
|
|||
/// When sliding the window, the contents of the bottom window band are copied to the new top band.
|
|||
/// For more details, and visual explanation, see "ResizeWorker.pptx".
|
|||
/// </summary>
|
|||
internal class ResizeWorker<TPixel> : IDisposable |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
private readonly Buffer2D<Vector4> transposedFirstPassBuffer; |
|||
|
|||
private readonly Configuration configuration; |
|||
|
|||
private readonly PixelConversionModifiers conversionModifiers; |
|||
|
|||
private readonly ResizeKernelMap horizontalKernelMap; |
|||
|
|||
private readonly BufferArea<TPixel> source; |
|||
|
|||
private readonly Rectangle sourceRectangle; |
|||
|
|||
private readonly IMemoryOwner<Vector4> tempRowBuffer; |
|||
|
|||
private readonly IMemoryOwner<Vector4> tempColumnBuffer; |
|||
|
|||
private readonly ResizeKernelMap verticalKernelMap; |
|||
|
|||
private readonly int destWidth; |
|||
|
|||
private readonly Rectangle targetWorkingRect; |
|||
|
|||
private readonly Point targetOrigin; |
|||
|
|||
private readonly int windowBandHeight; |
|||
|
|||
private readonly int workerHeight; |
|||
|
|||
private RowInterval currentWindow; |
|||
|
|||
public ResizeWorker( |
|||
Configuration configuration, |
|||
BufferArea<TPixel> source, |
|||
PixelConversionModifiers conversionModifiers, |
|||
ResizeKernelMap horizontalKernelMap, |
|||
ResizeKernelMap verticalKernelMap, |
|||
int destWidth, |
|||
Rectangle targetWorkingRect, |
|||
Point targetOrigin) |
|||
{ |
|||
this.configuration = configuration; |
|||
this.source = source; |
|||
this.sourceRectangle = source.Rectangle; |
|||
this.conversionModifiers = conversionModifiers; |
|||
this.horizontalKernelMap = horizontalKernelMap; |
|||
this.verticalKernelMap = verticalKernelMap; |
|||
this.destWidth = destWidth; |
|||
this.targetWorkingRect = targetWorkingRect; |
|||
this.targetOrigin = targetOrigin; |
|||
|
|||
this.windowBandHeight = verticalKernelMap.MaxDiameter; |
|||
|
|||
int numberOfWindowBands = ResizeHelper.CalculateResizeWorkerHeightInWindowBands( |
|||
this.windowBandHeight, |
|||
destWidth, |
|||
configuration.WorkingBufferSizeHintInBytes); |
|||
|
|||
this.workerHeight = Math.Min(this.sourceRectangle.Height, numberOfWindowBands * this.windowBandHeight); |
|||
|
|||
this.transposedFirstPassBuffer = configuration.MemoryAllocator.Allocate2D<Vector4>( |
|||
this.workerHeight, |
|||
destWidth, |
|||
AllocationOptions.Clean); |
|||
|
|||
this.tempRowBuffer = configuration.MemoryAllocator.Allocate<Vector4>(this.sourceRectangle.Width); |
|||
this.tempColumnBuffer = configuration.MemoryAllocator.Allocate<Vector4>(destWidth); |
|||
|
|||
this.currentWindow = new RowInterval(0, this.workerHeight); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
this.transposedFirstPassBuffer.Dispose(); |
|||
this.tempRowBuffer.Dispose(); |
|||
this.tempColumnBuffer.Dispose(); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public Span<Vector4> GetColumnSpan(int x, int startY) |
|||
{ |
|||
return this.transposedFirstPassBuffer.GetRowSpan(x).Slice(startY - this.currentWindow.Min); |
|||
} |
|||
|
|||
public void Initialize() |
|||
{ |
|||
this.CalculateFirstPassValues(this.currentWindow); |
|||
} |
|||
|
|||
public void FillDestinationPixels(RowInterval rowInterval, Buffer2D<TPixel> destination) |
|||
{ |
|||
Span<Vector4> tempColSpan = this.tempColumnBuffer.GetSpan(); |
|||
|
|||
for (int y = rowInterval.Min; y < rowInterval.Max; y++) |
|||
{ |
|||
// Ensure offsets are normalized for cropping and padding.
|
|||
ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - this.targetOrigin.Y); |
|||
|
|||
if (kernel.StartIndex + kernel.Length > this.currentWindow.Max) |
|||
{ |
|||
this.Slide(); |
|||
} |
|||
|
|||
ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); |
|||
|
|||
int top = kernel.StartIndex - this.currentWindow.Min; |
|||
|
|||
ref Vector4 fpBase = ref this.transposedFirstPassBuffer.Span[top]; |
|||
|
|||
for (int x = 0; x < this.destWidth; x++) |
|||
{ |
|||
ref Vector4 firstPassColumnBase = ref Unsafe.Add(ref fpBase, x * this.workerHeight); |
|||
|
|||
// Destination color components
|
|||
Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(ref firstPassColumnBase); |
|||
} |
|||
|
|||
Span<TPixel> targetRowSpan = destination.GetRowSpan(y); |
|||
|
|||
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, tempColSpan, targetRowSpan, this.conversionModifiers); |
|||
} |
|||
} |
|||
|
|||
private void Slide() |
|||
{ |
|||
int minY = this.currentWindow.Max - this.windowBandHeight; |
|||
int maxY = Math.Min(minY + this.workerHeight, this.sourceRectangle.Height); |
|||
|
|||
// Copy previous bottom band to the new top:
|
|||
// (rows <--> columns, because the buffer is transposed)
|
|||
this.transposedFirstPassBuffer.CopyColumns( |
|||
this.workerHeight - this.windowBandHeight, |
|||
0, |
|||
this.windowBandHeight); |
|||
|
|||
this.currentWindow = new RowInterval(minY, maxY); |
|||
|
|||
// Calculate the remainder:
|
|||
this.CalculateFirstPassValues(this.currentWindow.Slice(this.windowBandHeight)); |
|||
} |
|||
|
|||
private void CalculateFirstPassValues(RowInterval calculationInterval) |
|||
{ |
|||
Span<Vector4> tempRowSpan = this.tempRowBuffer.GetSpan(); |
|||
for (int y = calculationInterval.Min; y < calculationInterval.Max; y++) |
|||
{ |
|||
Span<TPixel> sourceRow = this.source.GetRowSpan(y); |
|||
|
|||
PixelOperations<TPixel>.Instance.ToVector4( |
|||
this.configuration, |
|||
sourceRow, |
|||
tempRowSpan, |
|||
this.conversionModifiers); |
|||
|
|||
// Span<Vector4> firstPassSpan = this.transposedFirstPassBuffer.Span.Slice(y - this.currentWindow.Min);
|
|||
ref Vector4 firstPassBaseRef = ref this.transposedFirstPassBuffer.Span[y - this.currentWindow.Min]; |
|||
|
|||
for (int x = this.targetWorkingRect.Left; x < this.targetWorkingRect.Right; x++) |
|||
{ |
|||
ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.targetOrigin.X); |
|||
|
|||
// firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan);
|
|||
Unsafe.Add(ref firstPassBaseRef, x * this.workerHeight) = kernel.Convolve(tempRowSpan); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Binary file not shown.
@ -1,102 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
namespace SixLabors.ImageSharp.Benchmarks.General |
|||
{ |
|||
[Config(typeof(Config.ShortClr))] |
|||
public class ArrayCopy |
|||
{ |
|||
[Params(10, 100, 1000, 10000)] |
|||
public int Count { get; set; } |
|||
|
|||
byte[] source; |
|||
|
|||
byte[] destination; |
|||
|
|||
[GlobalSetup] |
|||
public void SetUp() |
|||
{ |
|||
this.source = new byte[this.Count]; |
|||
this.destination = new byte[this.Count]; |
|||
} |
|||
|
|||
[Benchmark(Baseline = true, Description = "Copy using Array.Copy()")] |
|||
public void CopyArray() |
|||
{ |
|||
Array.Copy(this.source, this.destination, this.Count); |
|||
} |
|||
|
|||
[Benchmark(Description = "Copy using Unsafe<T>")] |
|||
public unsafe void CopyUnsafe() |
|||
{ |
|||
fixed (byte* pinnedDestination = this.destination) |
|||
fixed (byte* pinnedSource = this.source) |
|||
{ |
|||
Unsafe.CopyBlock(pinnedSource, pinnedDestination, (uint)this.Count); |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "Copy using Buffer.BlockCopy()")] |
|||
public void CopyUsingBufferBlockCopy() |
|||
{ |
|||
Buffer.BlockCopy(this.source, 0, this.destination, 0, this.Count); |
|||
} |
|||
|
|||
[Benchmark(Description = "Copy using Buffer.MemoryCopy<T>")] |
|||
public unsafe void CopyUsingBufferMemoryCopy() |
|||
{ |
|||
fixed (byte* pinnedDestination = this.destination) |
|||
fixed (byte* pinnedSource = this.source) |
|||
{ |
|||
Buffer.MemoryCopy(pinnedSource, pinnedDestination, this.Count, this.Count); |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "Copy using Marshal.Copy<T>")] |
|||
public unsafe void CopyUsingMarshalCopy() |
|||
{ |
|||
fixed (byte* pinnedDestination = this.destination) |
|||
{ |
|||
Marshal.Copy(this.source, 0, (IntPtr)pinnedDestination, this.Count); |
|||
} |
|||
} |
|||
|
|||
/***************************************************************************************************************** |
|||
*************** RESULTS on i7-4810MQ 2.80GHz + Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1085.0 ******************** |
|||
***************************************************************************************************************** |
|||
* |
|||
* Method | Count | Mean | StdErr | StdDev | Scaled | Scaled-StdDev | |
|||
* ---------------------------------- |------ |------------ |----------- |----------- |------- |-------------- | |
|||
* 'Copy using Array.Copy()' | 10 | 20.3074 ns | 0.1194 ns | 0.2068 ns | 1.00 | 0.00 | |
|||
* 'Copy using Unsafe<T>' | 10 | 6.1002 ns | 0.1981 ns | 0.3432 ns | 0.30 | 0.01 | |
|||
* 'Copy using Buffer.BlockCopy()' | 10 | 10.7879 ns | 0.0984 ns | 0.1705 ns | 0.53 | 0.01 | |
|||
* 'Copy using Buffer.MemoryCopy<T>' | 10 | 4.9625 ns | 0.0200 ns | 0.0347 ns | 0.24 | 0.00 | |
|||
* 'Copy using Marshal.Copy<T>' | 10 | 16.1782 ns | 0.0919 ns | 0.1592 ns | 0.80 | 0.01 | |
|||
* |
|||
* 'Copy using Array.Copy()' | 100 | 31.5945 ns | 0.2908 ns | 0.5037 ns | 1.00 | 0.00 | |
|||
* 'Copy using Unsafe<T>' | 100 | 10.2722 ns | 0.5202 ns | 0.9010 ns | 0.33 | 0.02 | |
|||
* 'Copy using Buffer.BlockCopy()' | 100 | 22.0322 ns | 0.0284 ns | 0.0493 ns | 0.70 | 0.01 | |
|||
* 'Copy using Buffer.MemoryCopy<T>' | 100 | 10.2472 ns | 0.0359 ns | 0.0622 ns | 0.32 | 0.00 | |
|||
* 'Copy using Marshal.Copy<T>' | 100 | 34.3820 ns | 1.1868 ns | 2.0555 ns | 1.09 | 0.05 | |
|||
* |
|||
* 'Copy using Array.Copy()' | 1000 | 40.9743 ns | 0.0521 ns | 0.0902 ns | 1.00 | 0.00 | |
|||
* 'Copy using Unsafe<T>' | 1000 | 42.7840 ns | 2.0139 ns | 3.4882 ns | 1.04 | 0.07 | |
|||
* 'Copy using Buffer.BlockCopy()' | 1000 | 33.7361 ns | 0.0751 ns | 0.1300 ns | 0.82 | 0.00 | |
|||
* 'Copy using Buffer.MemoryCopy<T>' | 1000 | 35.7541 ns | 0.0480 ns | 0.0832 ns | 0.87 | 0.00 | |
|||
* 'Copy using Marshal.Copy<T>' | 1000 | 42.2028 ns | 0.2769 ns | 0.4795 ns | 1.03 | 0.01 | |
|||
* |
|||
* 'Copy using Array.Copy()' | 10000 | 200.0438 ns | 0.2251 ns | 0.3899 ns | 1.00 | 0.00 | |
|||
* 'Copy using Unsafe<T>' | 10000 | 389.6957 ns | 13.2770 ns | 22.9964 ns | 1.95 | 0.09 | |
|||
* 'Copy using Buffer.BlockCopy()' | 10000 | 191.3478 ns | 0.1557 ns | 0.2697 ns | 0.96 | 0.00 | |
|||
* 'Copy using Buffer.MemoryCopy<T>' | 10000 | 196.4679 ns | 0.2731 ns | 0.4730 ns | 0.98 | 0.00 | |
|||
* 'Copy using Marshal.Copy<T>' | 10000 | 202.5392 ns | 0.5561 ns | 0.9631 ns | 1.01 | 0.00 | |
|||
* |
|||
*/ |
|||
} |
|||
} |
|||
@ -0,0 +1,231 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
namespace SixLabors.ImageSharp.Benchmarks.General |
|||
{ |
|||
/// <summary>
|
|||
/// Compare different methods for copying native and/or managed buffers.
|
|||
/// Conclusions:
|
|||
/// - Span.CopyTo() has terrible performance on classic .NET Framework
|
|||
/// - Buffer.MemoryCopy() performance is good enough for all sizes (but needs pinning)
|
|||
/// </summary>
|
|||
[Config(typeof(Config.ShortClr))] |
|||
public class CopyBuffers |
|||
{ |
|||
private byte[] destArray; |
|||
|
|||
private MemoryHandle destHandle; |
|||
|
|||
private Memory<byte> destMemory; |
|||
|
|||
private byte[] sourceArray; |
|||
|
|||
private MemoryHandle sourceHandle; |
|||
|
|||
private Memory<byte> sourceMemory; |
|||
|
|||
[Params(10, 50, 100, 1000, 10000)] |
|||
public int Count { get; set; } |
|||
|
|||
|
|||
[GlobalSetup] |
|||
public void Setup() |
|||
{ |
|||
this.sourceArray = new byte[this.Count]; |
|||
this.sourceMemory = new Memory<byte>(this.sourceArray); |
|||
this.sourceHandle = this.sourceMemory.Pin(); |
|||
|
|||
this.destArray = new byte[this.Count]; |
|||
this.destMemory = new Memory<byte>(this.destArray); |
|||
this.destHandle = this.destMemory.Pin(); |
|||
} |
|||
|
|||
[GlobalCleanup] |
|||
public void Cleanup() |
|||
{ |
|||
this.sourceHandle.Dispose(); |
|||
this.destHandle.Dispose(); |
|||
} |
|||
|
|||
[Benchmark(Baseline = true, Description = "Array.Copy()")] |
|||
public void ArrayCopy() |
|||
{ |
|||
Array.Copy(this.sourceArray, this.destArray, this.Count); |
|||
} |
|||
|
|||
[Benchmark(Description = "Buffer.BlockCopy()")] |
|||
public void BufferBlockCopy() |
|||
{ |
|||
Buffer.BlockCopy(this.sourceArray, 0, this.destArray, 0, this.Count); |
|||
} |
|||
|
|||
[Benchmark(Description = "Buffer.MemoryCopy()")] |
|||
public unsafe void BufferMemoryCopy() |
|||
{ |
|||
void* pinnedDestination = this.destHandle.Pointer; |
|||
void* pinnedSource = this.sourceHandle.Pointer; |
|||
Buffer.MemoryCopy(pinnedSource, pinnedDestination, this.Count, this.Count); |
|||
} |
|||
|
|||
|
|||
[Benchmark(Description = "Marshal.Copy()")] |
|||
public unsafe void MarshalCopy() |
|||
{ |
|||
void* pinnedDestination = this.destHandle.Pointer; |
|||
Marshal.Copy(this.sourceArray, 0, (IntPtr)pinnedDestination, this.Count); |
|||
} |
|||
|
|||
[Benchmark(Description = "Span.CopyTo()")] |
|||
public void SpanCopyTo() |
|||
{ |
|||
this.sourceMemory.Span.CopyTo(this.destMemory.Span); |
|||
} |
|||
|
|||
[Benchmark(Description = "Unsafe.CopyBlock(ref)")] |
|||
public unsafe void UnsafeCopyBlockReferences() |
|||
{ |
|||
Unsafe.CopyBlock(ref this.destArray[0], ref this.sourceArray[0], (uint)this.Count); |
|||
} |
|||
|
|||
[Benchmark(Description = "Unsafe.CopyBlock(ptr)")] |
|||
public unsafe void UnsafeCopyBlockPointers() |
|||
{ |
|||
void* pinnedDestination = this.destHandle.Pointer; |
|||
void* pinnedSource = this.sourceHandle.Pointer; |
|||
Unsafe.CopyBlock(pinnedDestination, pinnedSource, (uint)this.Count); |
|||
} |
|||
|
|||
[Benchmark(Description = "Unsafe.CopyBlockUnaligned(ref)")] |
|||
public unsafe void UnsafeCopyBlockUnalignedReferences() |
|||
{ |
|||
Unsafe.CopyBlockUnaligned(ref this.destArray[0], ref this.sourceArray[0], (uint)this.Count); |
|||
} |
|||
|
|||
[Benchmark(Description = "Unsafe.CopyBlockUnaligned(ptr)")] |
|||
public unsafe void UnsafeCopyBlockUnalignedPointers() |
|||
{ |
|||
void* pinnedDestination = this.destHandle.Pointer; |
|||
void* pinnedSource = this.sourceHandle.Pointer; |
|||
Unsafe.CopyBlockUnaligned(pinnedDestination, pinnedSource, (uint)this.Count); |
|||
} |
|||
|
|||
// BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.706 (1803/April2018Update/Redstone4)
|
|||
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
|
|||
// Frequency=2742189 Hz, Resolution=364.6722 ns, Timer=TSC
|
|||
// .NET Core SDK=2.2.202
|
|||
// [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
|
|||
// Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3394.0
|
|||
// Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
|
|||
//
|
|||
// IterationCount=3 LaunchCount=1 WarmupCount=3
|
|||
//
|
|||
// | Method | Job | Runtime | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
|
|||
// |------------------------------- |----- |-------- |------ |-----------:|-----------:|----------:|------:|--------:|------:|------:|------:|----------:|
|
|||
// | Array.Copy() | Clr | Clr | 10 | 23.636 ns | 2.5299 ns | 0.1387 ns | 1.00 | 0.00 | - | - | - | - |
|
|||
// | Buffer.BlockCopy() | Clr | Clr | 10 | 11.420 ns | 2.3341 ns | 0.1279 ns | 0.48 | 0.01 | - | - | - | - |
|
|||
// | Buffer.MemoryCopy() | Clr | Clr | 10 | 2.861 ns | 0.5059 ns | 0.0277 ns | 0.12 | 0.00 | - | - | - | - |
|
|||
// | Marshal.Copy() | Clr | Clr | 10 | 14.870 ns | 2.4541 ns | 0.1345 ns | 0.63 | 0.01 | - | - | - | - |
|
|||
// | Span.CopyTo() | Clr | Clr | 10 | 31.906 ns | 1.2213 ns | 0.0669 ns | 1.35 | 0.01 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ref) | Clr | Clr | 10 | 3.513 ns | 0.7392 ns | 0.0405 ns | 0.15 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ptr) | Clr | Clr | 10 | 3.053 ns | 0.2010 ns | 0.0110 ns | 0.13 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 10 | 3.497 ns | 0.4911 ns | 0.0269 ns | 0.15 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 10 | 3.109 ns | 0.5958 ns | 0.0327 ns | 0.13 | 0.00 | - | - | - | - |
|
|||
// | | | | | | | | | | | | | |
|
|||
// | Array.Copy() | Core | Core | 10 | 19.709 ns | 2.1867 ns | 0.1199 ns | 1.00 | 0.00 | - | - | - | - |
|
|||
// | Buffer.BlockCopy() | Core | Core | 10 | 7.377 ns | 1.1582 ns | 0.0635 ns | 0.37 | 0.01 | - | - | - | - |
|
|||
// | Buffer.MemoryCopy() | Core | Core | 10 | 2.581 ns | 1.1607 ns | 0.0636 ns | 0.13 | 0.00 | - | - | - | - |
|
|||
// | Marshal.Copy() | Core | Core | 10 | 15.197 ns | 2.8446 ns | 0.1559 ns | 0.77 | 0.01 | - | - | - | - |
|
|||
// | Span.CopyTo() | Core | Core | 10 | 25.394 ns | 0.9782 ns | 0.0536 ns | 1.29 | 0.01 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ref) | Core | Core | 10 | 2.254 ns | 0.1590 ns | 0.0087 ns | 0.11 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ptr) | Core | Core | 10 | 1.878 ns | 0.1035 ns | 0.0057 ns | 0.10 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 10 | 2.263 ns | 0.1383 ns | 0.0076 ns | 0.11 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 10 | 1.877 ns | 0.0602 ns | 0.0033 ns | 0.10 | 0.00 | - | - | - | - |
|
|||
// | | | | | | | | | | | | | |
|
|||
// | Array.Copy() | Clr | Clr | 50 | 35.068 ns | 5.9137 ns | 0.3242 ns | 1.00 | 0.00 | - | - | - | - |
|
|||
// | Buffer.BlockCopy() | Clr | Clr | 50 | 23.299 ns | 2.3797 ns | 0.1304 ns | 0.66 | 0.01 | - | - | - | - |
|
|||
// | Buffer.MemoryCopy() | Clr | Clr | 50 | 3.598 ns | 4.8536 ns | 0.2660 ns | 0.10 | 0.01 | - | - | - | - |
|
|||
// | Marshal.Copy() | Clr | Clr | 50 | 27.720 ns | 4.6602 ns | 0.2554 ns | 0.79 | 0.01 | - | - | - | - |
|
|||
// | Span.CopyTo() | Clr | Clr | 50 | 35.673 ns | 16.2972 ns | 0.8933 ns | 1.02 | 0.03 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ref) | Clr | Clr | 50 | 5.534 ns | 2.8119 ns | 0.1541 ns | 0.16 | 0.01 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ptr) | Clr | Clr | 50 | 4.511 ns | 0.9555 ns | 0.0524 ns | 0.13 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 50 | 5.613 ns | 1.6679 ns | 0.0914 ns | 0.16 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 50 | 4.884 ns | 7.3153 ns | 0.4010 ns | 0.14 | 0.01 | - | - | - | - |
|
|||
// | | | | | | | | | | | | | |
|
|||
// | Array.Copy() | Core | Core | 50 | 20.232 ns | 1.5720 ns | 0.0862 ns | 1.00 | 0.00 | - | - | - | - |
|
|||
// | Buffer.BlockCopy() | Core | Core | 50 | 8.142 ns | 0.7860 ns | 0.0431 ns | 0.40 | 0.00 | - | - | - | - |
|
|||
// | Buffer.MemoryCopy() | Core | Core | 50 | 2.962 ns | 0.0611 ns | 0.0033 ns | 0.15 | 0.00 | - | - | - | - |
|
|||
// | Marshal.Copy() | Core | Core | 50 | 16.802 ns | 2.9686 ns | 0.1627 ns | 0.83 | 0.00 | - | - | - | - |
|
|||
// | Span.CopyTo() | Core | Core | 50 | 26.571 ns | 0.9228 ns | 0.0506 ns | 1.31 | 0.01 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ref) | Core | Core | 50 | 2.219 ns | 0.7191 ns | 0.0394 ns | 0.11 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ptr) | Core | Core | 50 | 1.751 ns | 0.1884 ns | 0.0103 ns | 0.09 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 50 | 2.177 ns | 0.4489 ns | 0.0246 ns | 0.11 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 50 | 1.806 ns | 0.1063 ns | 0.0058 ns | 0.09 | 0.00 | - | - | - | - |
|
|||
// | | | | | | | | | | | | | |
|
|||
// | Array.Copy() | Clr | Clr | 100 | 39.158 ns | 4.3068 ns | 0.2361 ns | 1.00 | 0.00 | - | - | - | - |
|
|||
// | Buffer.BlockCopy() | Clr | Clr | 100 | 27.623 ns | 0.4611 ns | 0.0253 ns | 0.71 | 0.00 | - | - | - | - |
|
|||
// | Buffer.MemoryCopy() | Clr | Clr | 100 | 5.018 ns | 0.5689 ns | 0.0312 ns | 0.13 | 0.00 | - | - | - | - |
|
|||
// | Marshal.Copy() | Clr | Clr | 100 | 33.527 ns | 1.9019 ns | 0.1042 ns | 0.86 | 0.01 | - | - | - | - |
|
|||
// | Span.CopyTo() | Clr | Clr | 100 | 35.604 ns | 2.7039 ns | 0.1482 ns | 0.91 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ref) | Clr | Clr | 100 | 7.853 ns | 0.4925 ns | 0.0270 ns | 0.20 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ptr) | Clr | Clr | 100 | 7.406 ns | 1.9733 ns | 0.1082 ns | 0.19 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 100 | 7.822 ns | 0.6837 ns | 0.0375 ns | 0.20 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 100 | 7.392 ns | 1.2832 ns | 0.0703 ns | 0.19 | 0.00 | - | - | - | - |
|
|||
// | | | | | | | | | | | | | |
|
|||
// | Array.Copy() | Core | Core | 100 | 22.909 ns | 2.9754 ns | 0.1631 ns | 1.00 | 0.00 | - | - | - | - |
|
|||
// | Buffer.BlockCopy() | Core | Core | 100 | 10.687 ns | 1.1262 ns | 0.0617 ns | 0.47 | 0.00 | - | - | - | - |
|
|||
// | Buffer.MemoryCopy() | Core | Core | 100 | 4.063 ns | 0.1607 ns | 0.0088 ns | 0.18 | 0.00 | - | - | - | - |
|
|||
// | Marshal.Copy() | Core | Core | 100 | 18.067 ns | 4.0557 ns | 0.2223 ns | 0.79 | 0.01 | - | - | - | - |
|
|||
// | Span.CopyTo() | Core | Core | 100 | 28.352 ns | 1.2762 ns | 0.0700 ns | 1.24 | 0.01 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ref) | Core | Core | 100 | 4.130 ns | 0.2013 ns | 0.0110 ns | 0.18 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ptr) | Core | Core | 100 | 4.096 ns | 0.2460 ns | 0.0135 ns | 0.18 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 100 | 4.160 ns | 0.3174 ns | 0.0174 ns | 0.18 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 100 | 3.480 ns | 1.1683 ns | 0.0640 ns | 0.15 | 0.00 | - | - | - | - |
|
|||
// | | | | | | | | | | | | | |
|
|||
// | Array.Copy() | Clr | Clr | 1000 | 49.059 ns | 2.0729 ns | 0.1136 ns | 1.00 | 0.00 | - | - | - | - |
|
|||
// | Buffer.BlockCopy() | Clr | Clr | 1000 | 38.270 ns | 23.6970 ns | 1.2989 ns | 0.78 | 0.03 | - | - | - | - |
|
|||
// | Buffer.MemoryCopy() | Clr | Clr | 1000 | 27.599 ns | 6.8328 ns | 0.3745 ns | 0.56 | 0.01 | - | - | - | - |
|
|||
// | Marshal.Copy() | Clr | Clr | 1000 | 42.752 ns | 5.1357 ns | 0.2815 ns | 0.87 | 0.01 | - | - | - | - |
|
|||
// | Span.CopyTo() | Clr | Clr | 1000 | 69.983 ns | 2.1860 ns | 0.1198 ns | 1.43 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ref) | Clr | Clr | 1000 | 44.822 ns | 0.1625 ns | 0.0089 ns | 0.91 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ptr) | Clr | Clr | 1000 | 45.072 ns | 1.4053 ns | 0.0770 ns | 0.92 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 1000 | 45.306 ns | 5.2646 ns | 0.2886 ns | 0.92 | 0.01 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 1000 | 44.813 ns | 0.9117 ns | 0.0500 ns | 0.91 | 0.00 | - | - | - | - |
|
|||
// | | | | | | | | | | | | | |
|
|||
// | Array.Copy() | Core | Core | 1000 | 51.907 ns | 3.1827 ns | 0.1745 ns | 1.00 | 0.00 | - | - | - | - |
|
|||
// | Buffer.BlockCopy() | Core | Core | 1000 | 40.700 ns | 3.1488 ns | 0.1726 ns | 0.78 | 0.00 | - | - | - | - |
|
|||
// | Buffer.MemoryCopy() | Core | Core | 1000 | 23.711 ns | 1.3004 ns | 0.0713 ns | 0.46 | 0.00 | - | - | - | - |
|
|||
// | Marshal.Copy() | Core | Core | 1000 | 42.586 ns | 2.5390 ns | 0.1392 ns | 0.82 | 0.00 | - | - | - | - |
|
|||
// | Span.CopyTo() | Core | Core | 1000 | 44.109 ns | 4.5604 ns | 0.2500 ns | 0.85 | 0.01 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ref) | Core | Core | 1000 | 33.926 ns | 5.1633 ns | 0.2830 ns | 0.65 | 0.01 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ptr) | Core | Core | 1000 | 33.267 ns | 0.2708 ns | 0.0148 ns | 0.64 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 1000 | 34.018 ns | 2.3238 ns | 0.1274 ns | 0.66 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 1000 | 33.667 ns | 2.1983 ns | 0.1205 ns | 0.65 | 0.00 | - | - | - | - |
|
|||
// | | | | | | | | | | | | | |
|
|||
// | Array.Copy() | Clr | Clr | 10000 | 153.429 ns | 6.1735 ns | 0.3384 ns | 1.00 | 0.00 | - | - | - | - |
|
|||
// | Buffer.BlockCopy() | Clr | Clr | 10000 | 201.373 ns | 4.3670 ns | 0.2394 ns | 1.31 | 0.00 | - | - | - | - |
|
|||
// | Buffer.MemoryCopy() | Clr | Clr | 10000 | 211.768 ns | 71.3510 ns | 3.9110 ns | 1.38 | 0.02 | - | - | - | - |
|
|||
// | Marshal.Copy() | Clr | Clr | 10000 | 215.299 ns | 17.2677 ns | 0.9465 ns | 1.40 | 0.01 | - | - | - | - |
|
|||
// | Span.CopyTo() | Clr | Clr | 10000 | 486.325 ns | 32.4445 ns | 1.7784 ns | 3.17 | 0.01 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ref) | Clr | Clr | 10000 | 452.314 ns | 33.0593 ns | 1.8121 ns | 2.95 | 0.02 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ptr) | Clr | Clr | 10000 | 455.600 ns | 56.7534 ns | 3.1108 ns | 2.97 | 0.02 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ref) | Clr | Clr | 10000 | 452.279 ns | 8.6457 ns | 0.4739 ns | 2.95 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ptr) | Clr | Clr | 10000 | 453.146 ns | 12.3776 ns | 0.6785 ns | 2.95 | 0.00 | - | - | - | - |
|
|||
// | | | | | | | | | | | | | |
|
|||
// | Array.Copy() | Core | Core | 10000 | 204.508 ns | 3.1652 ns | 0.1735 ns | 1.00 | 0.00 | - | - | - | - |
|
|||
// | Buffer.BlockCopy() | Core | Core | 10000 | 193.345 ns | 1.3742 ns | 0.0753 ns | 0.95 | 0.00 | - | - | - | - |
|
|||
// | Buffer.MemoryCopy() | Core | Core | 10000 | 196.978 ns | 18.3279 ns | 1.0046 ns | 0.96 | 0.01 | - | - | - | - |
|
|||
// | Marshal.Copy() | Core | Core | 10000 | 206.878 ns | 6.9938 ns | 0.3834 ns | 1.01 | 0.00 | - | - | - | - |
|
|||
// | Span.CopyTo() | Core | Core | 10000 | 215.733 ns | 15.4824 ns | 0.8486 ns | 1.05 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ref) | Core | Core | 10000 | 186.894 ns | 8.7617 ns | 0.4803 ns | 0.91 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlock(ptr) | Core | Core | 10000 | 186.662 ns | 10.6059 ns | 0.5813 ns | 0.91 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ref) | Core | Core | 10000 | 187.489 ns | 13.1527 ns | 0.7209 ns | 0.92 | 0.00 | - | - | - | - |
|
|||
// | Unsafe.CopyBlockUnaligned(ptr) | Core | Core | 10000 | 186.586 ns | 4.6274 ns | 0.2536 ns | 0.91 | 0.00 | - | - | - | - |
|
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
// // Copyright (c) Six Labors and contributors.
|
|||
// // Licensed under the Apache License, Version 2.0.
|
|||
// // Copyright (c) Six Labors and contributors.
|
|||
// // Licensed under the Apache License, Version 2.0.
|
|||
// // Copyright (c) Six Labors and contributors.
|
|||
// // Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
using Xunit; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations |
|||
{ |
|||
public class PixelConversionModifiersExtensionsTests |
|||
{ |
|||
[Theory] |
|||
[InlineData(PixelConversionModifiers.None, PixelConversionModifiers.None, true)] |
|||
[InlineData(PixelConversionModifiers.None, PixelConversionModifiers.Premultiply, false)] |
|||
[InlineData(PixelConversionModifiers.SRgbCompand, PixelConversionModifiers.Premultiply, false)] |
|||
[InlineData( |
|||
PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale, |
|||
PixelConversionModifiers.Premultiply, |
|||
true)] |
|||
[InlineData( |
|||
PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale, |
|||
PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale, |
|||
true)] |
|||
[InlineData( |
|||
PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale, |
|||
PixelConversionModifiers.Scale, |
|||
true)] |
|||
internal void IsDefined( |
|||
PixelConversionModifiers baselineModifiers, |
|||
PixelConversionModifiers checkModifiers, |
|||
bool expected) |
|||
{ |
|||
Assert.Equal(expected, baselineModifiers.IsDefined(checkModifiers)); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand, |
|||
PixelConversionModifiers.Scale, PixelConversionModifiers.Premultiply | PixelConversionModifiers.SRgbCompand)] |
|||
[InlineData(PixelConversionModifiers.None, PixelConversionModifiers.Premultiply, PixelConversionModifiers.None)] |
|||
internal void Remove( |
|||
PixelConversionModifiers baselineModifiers, |
|||
PixelConversionModifiers toRemove, |
|||
PixelConversionModifiers expected) |
|||
{ |
|||
PixelConversionModifiers result = baselineModifiers.Remove(toRemove); |
|||
Assert.Equal(expected, result); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(PixelConversionModifiers.Premultiply, false, PixelConversionModifiers.Premultiply)] |
|||
[InlineData(PixelConversionModifiers.Premultiply, true, PixelConversionModifiers.Premultiply | PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale)] |
|||
internal void ApplyCompanding( |
|||
PixelConversionModifiers baselineModifiers, |
|||
bool compand, |
|||
PixelConversionModifiers expected) |
|||
{ |
|||
PixelConversionModifiers result = baselineModifiers.ApplyCompanding(compand); |
|||
Assert.Equal(expected, result); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Processing; |
|||
using SixLabors.ImageSharp.Processing.Processors.Transforms; |
|||
|
|||
using Xunit; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms |
|||
{ |
|||
public class ResamplerTests |
|||
{ |
|||
[Theory] |
|||
[InlineData(-2, 0)] |
|||
[InlineData(-1, 0)] |
|||
[InlineData(0, 1)] |
|||
[InlineData(1, 0)] |
|||
[InlineData(2, 0)] |
|||
public static void BicubicWindowOscillatesCorrectly(float x, float expected) |
|||
{ |
|||
IResampler sampler = KnownResamplers.Bicubic; |
|||
float result = sampler.GetValue(x); |
|||
|
|||
Assert.Equal(result, expected); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(-2, 0)] |
|||
[InlineData(-1, 0)] |
|||
[InlineData(0, 1)] |
|||
[InlineData(1, 0)] |
|||
[InlineData(2, 0)] |
|||
public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) |
|||
{ |
|||
IResampler sampler = KnownResamplers.Lanczos3; |
|||
float result = sampler.GetValue(x); |
|||
|
|||
Assert.Equal(result, expected); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(-4, 0)] |
|||
[InlineData(-2, 0)] |
|||
[InlineData(0, 1)] |
|||
[InlineData(2, 0)] |
|||
[InlineData(4, 0)] |
|||
public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) |
|||
{ |
|||
IResampler sampler = KnownResamplers.Lanczos5; |
|||
float result = sampler.GetValue(x); |
|||
|
|||
Assert.Equal(result, expected); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(-2, 0)] |
|||
[InlineData(-1, 0)] |
|||
[InlineData(0, 1)] |
|||
[InlineData(1, 0)] |
|||
[InlineData(2, 0)] |
|||
public static void TriangleWindowOscillatesCorrectly(float x, float expected) |
|||
{ |
|||
IResampler sampler = KnownResamplers.Triangle; |
|||
float result = sampler.GetValue(x); |
|||
|
|||
Assert.Equal(result, expected); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Processing.Processors.Transforms; |
|||
|
|||
using Xunit; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms |
|||
{ |
|||
public class ResizeHelperTests |
|||
{ |
|||
|
|||
[Theory] |
|||
[InlineData(20, 100, 1, 2)] |
|||
[InlineData(20, 100, 20*100*16, 2)] |
|||
[InlineData(20, 100, 40*100*16, 2)] |
|||
[InlineData(20, 100, 59*100*16, 2)] |
|||
[InlineData(20, 100, 60*100*16, 3)] |
|||
[InlineData(17, 63, 5*17*63*16, 5)] |
|||
[InlineData(17, 63, 5*17*63*16+1, 5)] |
|||
[InlineData(17, 63, 6*17*63*16-1, 5)] |
|||
[InlineData(33, 400, 1*1024*1024, 4)] |
|||
[InlineData(33, 400, 8*1024*1024, 39)] |
|||
[InlineData(50, 300, 1*1024*1024, 4)] |
|||
public void CalculateResizeWorkerHeightInWindowBands( |
|||
int windowDiameter, |
|||
int width, |
|||
int sizeLimitHintInBytes, |
|||
int expectedCount) |
|||
{ |
|||
int actualCount = ResizeHelper.CalculateResizeWorkerHeightInWindowBands(windowDiameter, width, sizeLimitHintInBytes); |
|||
Assert.Equal(expectedCount, actualCount); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Reflection; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests |
|||
{ |
|||
public class WithBasicTestPatternImagesAttribute : ImageDataAttributeBase |
|||
{ |
|||
public WithBasicTestPatternImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) |
|||
: this(null, width, height, pixelTypes, additionalParameters) |
|||
{ |
|||
} |
|||
|
|||
public WithBasicTestPatternImagesAttribute(string memberData, int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) |
|||
: base(memberData, pixelTypes, additionalParameters) |
|||
{ |
|||
this.Width = width; |
|||
this.Height = height; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the width
|
|||
/// </summary>
|
|||
public int Width { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the height
|
|||
/// </summary>
|
|||
public int Height { get; } |
|||
|
|||
protected override string GetFactoryMethodName(MethodInfo testMethod) => "BasicTestPattern"; |
|||
|
|||
protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => new object[] { this.Width, this.Height }; |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
|
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests |
|||
{ |
|||
public abstract partial class TestImageProvider<TPixel> |
|||
{ |
|||
private class BasicTestPatternProvider : BlankProvider |
|||
{ |
|||
public BasicTestPatternProvider(int width, int height) |
|||
: base(width, height) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This parameterless constructor is needed for xUnit deserialization
|
|||
/// </summary>
|
|||
public BasicTestPatternProvider() |
|||
{ |
|||
} |
|||
|
|||
public override string SourceFileOrDescription => TestUtils.AsInvariantString($"BasicTestPattern{this.Width}x{this.Height}"); |
|||
|
|||
public override Image<TPixel> GetImage() |
|||
{ |
|||
var result = new Image<TPixel>(this.Configuration, this.Width, this.Height); |
|||
|
|||
TPixel topLeftColor = NamedColors<TPixel>.Red; |
|||
TPixel topRightColor = NamedColors<TPixel>.Green; |
|||
TPixel bottomLeftColor = NamedColors<TPixel>.Blue; |
|||
|
|||
// Transparent purple:
|
|||
TPixel bottomRightColor = default; |
|||
bottomRightColor.FromVector4(new Vector4(1f, 0f, 1f, 0.5f)); |
|||
|
|||
int midY = this.Height / 2; |
|||
int midX = this.Width / 2; |
|||
|
|||
for (int y = 0; y < midY; y++) |
|||
{ |
|||
Span<TPixel> row = result.GetPixelRowSpan(y); |
|||
|
|||
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 = result.GetPixelRowSpan(y); |
|||
|
|||
row.Slice(0, midX).Fill(bottomLeftColor); |
|||
row.Slice(midX, this.Width-midX).Fill(bottomRightColor); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1 +1 @@ |
|||
Subproject commit 802725dec2a6b1ca02f9e2f9a4c3f625583d0696 |
|||
Subproject commit 8693e2fd4577a9ac1a749da8db564095b5a05389 |
|||
Loading…
Reference in new issue