mirror of https://github.com/SixLabors/ImageSharp
34 changed files with 1424 additions and 648 deletions
@ -0,0 +1,4 @@ |
|||
ignore: |
|||
"src/ImageSharp/Common/Helpers/DebugGuard.cs" |
|||
|
|||
|
|||
@ -0,0 +1,4 @@ |
|||
#recipe Docs |
|||
Settings[Keys.Host] = "imagesharp.org"; |
|||
Settings[Keys.Title] = "Image Sharp"; |
|||
FileSystem.OutputPath = "./docs"; |
|||
@ -0,0 +1,3 @@ |
|||
Title: About This Project |
|||
--- |
|||
This project is awesome! |
|||
@ -0,0 +1,249 @@ |
|||
// <copyright file="Color.BulkOperations.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
/// <content>
|
|||
/// Conains the definition of <see cref="BulkOperations"/>
|
|||
/// </content>
|
|||
public partial struct Color |
|||
{ |
|||
/// <summary>
|
|||
/// <see cref="BulkPixelOperations{TColor}"/> implementation optimized for <see cref="Color"/>.
|
|||
/// </summary>
|
|||
internal class BulkOperations : BulkPixelOperations<Color> |
|||
{ |
|||
/// <summary>
|
|||
/// SIMD optimized bulk implementation of <see cref="IPixel.PackFromVector4(Vector4)"/>
|
|||
/// that works only with `count` divisible by <see cref="Vector{UInt32}.Count"/>.
|
|||
/// </summary>
|
|||
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
|
|||
/// <param name="destVectors">The <see cref="BufferPointer{T}"/> to the dstination vectors.</param>
|
|||
/// <param name="count">The number of pixels to convert.</param>
|
|||
/// <remarks>
|
|||
/// Implementation adapted from:
|
|||
/// <see>
|
|||
/// <cref>http://stackoverflow.com/a/5362789</cref>
|
|||
/// </see>
|
|||
/// TODO: We can replace this implementation in the future using new Vector API-s:
|
|||
/// <see>
|
|||
/// <cref>https://github.com/dotnet/corefx/issues/15957</cref>
|
|||
/// </see>
|
|||
/// </remarks>
|
|||
internal static unsafe void ToVector4SimdAligned( |
|||
BufferPointer<Color> sourceColors, |
|||
BufferPointer<Vector4> destVectors, |
|||
int count) |
|||
{ |
|||
int vecSize = Vector<uint>.Count; |
|||
|
|||
DebugGuard.IsTrue( |
|||
count % vecSize == 0, |
|||
nameof(count), |
|||
"Argument 'count' should divisible by Vector<uint>.Count!"); |
|||
|
|||
Vector<float> bVec = new Vector<float>(256.0f / 255.0f); |
|||
Vector<float> magicFloat = new Vector<float>(32768.0f); |
|||
Vector<uint> magicInt = new Vector<uint>(1191182336); // reinterpreded value of 32768.0f
|
|||
Vector<uint> mask = new Vector<uint>(255); |
|||
|
|||
int unpackedRawCount = count * 4; |
|||
|
|||
uint* src = (uint*)sourceColors.PointerAtOffset; |
|||
uint* srcEnd = src + count; |
|||
|
|||
using (PinnedBuffer<uint> tempBuf = new PinnedBuffer<uint>( |
|||
unpackedRawCount + Vector<uint>.Count)) |
|||
{ |
|||
uint* tPtr = (uint*)tempBuf.Pointer; |
|||
uint[] temp = tempBuf.Array; |
|||
float[] fTemp = Unsafe.As<float[]>(temp); |
|||
UnpackedRGBA* dst = (UnpackedRGBA*)tPtr; |
|||
|
|||
for (; src < srcEnd; src++, dst++) |
|||
{ |
|||
// This call is the bottleneck now:
|
|||
dst->Load(*src); |
|||
} |
|||
|
|||
for (int i = 0; i < unpackedRawCount; i += vecSize) |
|||
{ |
|||
Vector<uint> vi = new Vector<uint>(temp, i); |
|||
|
|||
vi &= mask; |
|||
vi |= magicInt; |
|||
|
|||
Vector<float> vf = Vector.AsVectorSingle(vi); |
|||
vf = (vf - magicFloat) * bVec; |
|||
vf.CopyTo(fTemp, i); |
|||
} |
|||
|
|||
BufferPointer.Copy<uint>(tempBuf, (BufferPointer<byte>)destVectors, unpackedRawCount); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override void ToVector4(BufferPointer<Color> sourceColors, BufferPointer<Vector4> destVectors, int count) |
|||
{ |
|||
if (count < 256) |
|||
{ |
|||
// Doesn't worth to bother with SIMD:
|
|||
base.ToVector4(sourceColors, destVectors, count); |
|||
return; |
|||
} |
|||
|
|||
int remainder = count % Vector<uint>.Count; |
|||
|
|||
int alignedCount = count - remainder; |
|||
|
|||
if (alignedCount > 0) |
|||
{ |
|||
ToVector4SimdAligned(sourceColors, destVectors, alignedCount); |
|||
} |
|||
|
|||
if (remainder > 0) |
|||
{ |
|||
sourceColors = sourceColors.Slice(alignedCount); |
|||
destVectors = destVectors.Slice(alignedCount); |
|||
base.ToVector4(sourceColors, destVectors, remainder); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override unsafe void PackFromXyzBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count) |
|||
{ |
|||
byte* source = (byte*)sourceBytes; |
|||
byte* destination = (byte*)destColors; |
|||
|
|||
for (int x = 0; x < count; x++) |
|||
{ |
|||
Unsafe.Write(destination, (uint)(*source << 0 | *(source + 1) << 8 | *(source + 2) << 16 | 255 << 24)); |
|||
|
|||
source += 3; |
|||
destination += 4; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override unsafe void ToXyzBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count) |
|||
{ |
|||
byte* source = (byte*)sourceColors; |
|||
byte* destination = (byte*)destBytes; |
|||
|
|||
for (int x = 0; x < count; x++) |
|||
{ |
|||
*destination = *(source + 0); |
|||
*(destination + 1) = *(source + 1); |
|||
*(destination + 2) = *(source + 2); |
|||
|
|||
source += 4; |
|||
destination += 3; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override void PackFromXyzwBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count) |
|||
{ |
|||
BufferPointer.Copy(sourceBytes, destColors, count); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override void ToXyzwBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count) |
|||
{ |
|||
BufferPointer.Copy(sourceColors, destBytes, count); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override unsafe void PackFromZyxBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count) |
|||
{ |
|||
byte* source = (byte*)sourceBytes; |
|||
byte* destination = (byte*)destColors; |
|||
|
|||
for (int x = 0; x < count; x++) |
|||
{ |
|||
Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | 255 << 24)); |
|||
|
|||
source += 3; |
|||
destination += 4; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override unsafe void ToZyxBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count) |
|||
{ |
|||
byte* source = (byte*)sourceColors; |
|||
byte* destination = (byte*)destBytes; |
|||
|
|||
for (int x = 0; x < count; x++) |
|||
{ |
|||
*destination = *(source + 2); |
|||
*(destination + 1) = *(source + 1); |
|||
*(destination + 2) = *(source + 0); |
|||
|
|||
source += 4; |
|||
destination += 3; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override unsafe void PackFromZyxwBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count) |
|||
{ |
|||
byte* source = (byte*)sourceBytes; |
|||
byte* destination = (byte*)destColors; |
|||
|
|||
for (int x = 0; x < count; x++) |
|||
{ |
|||
Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | *(source + 3) << 24)); |
|||
|
|||
source += 4; |
|||
destination += 4; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override unsafe void ToZyxwBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count) |
|||
{ |
|||
byte* source = (byte*)sourceColors; |
|||
byte* destination = (byte*)destBytes; |
|||
|
|||
for (int x = 0; x < count; x++) |
|||
{ |
|||
*destination = *(source + 2); |
|||
*(destination + 1) = *(source + 1); |
|||
*(destination + 2) = *(source + 0); |
|||
*(destination + 3) = *(source + 3); |
|||
|
|||
source += 4; |
|||
destination += 4; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Value type to store <see cref="Color"/>-s unpacked into multiple <see cref="uint"/>-s.
|
|||
/// </summary>
|
|||
private struct UnpackedRGBA |
|||
{ |
|||
private uint r; |
|||
private uint g; |
|||
private uint b; |
|||
private uint a; |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void Load(uint p) |
|||
{ |
|||
this.r = p; |
|||
this.g = p >> Color.GreenShift; |
|||
this.b = p >> Color.BlueShift; |
|||
this.a = p >> Color.AlphaShift; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,52 +0,0 @@ |
|||
// <copyright file="ImageFrame.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System.Diagnostics; |
|||
|
|||
/// <summary>
|
|||
/// An optimized frame for the <see cref="Image"/> class.
|
|||
/// </summary>
|
|||
[DebuggerDisplay("ImageFrame: {Width}x{Height}")] |
|||
public sealed class ImageFrame : ImageFrame<Color> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ImageFrame"/> class.
|
|||
/// </summary>
|
|||
/// <param name="width">The width of the image in pixels.</param>
|
|||
/// <param name="height">The height of the image in pixels.</param>
|
|||
/// <param name="configuration">
|
|||
/// The configuration providing initialization code which allows extending the library.
|
|||
/// </param>
|
|||
public ImageFrame(int width, int height, Configuration configuration = null) |
|||
: base(width, height, configuration) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ImageFrame"/> class.
|
|||
/// </summary>
|
|||
/// <param name="image">
|
|||
/// The image to create the frame from.
|
|||
/// </param>
|
|||
public ImageFrame(ImageBase<Color> image) |
|||
: base(image) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override PixelAccessor<Color> Lock() |
|||
{ |
|||
return new PixelAccessor(this); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override ImageFrame<Color> Clone() |
|||
{ |
|||
return new ImageFrame(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,153 +0,0 @@ |
|||
// <copyright file="PixelAccessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
/// <summary>
|
|||
/// An optimized pixel accessor for the <see cref="Image"/> class.
|
|||
/// </summary>
|
|||
public sealed unsafe class PixelAccessor : PixelAccessor<Color> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PixelAccessor"/> class.
|
|||
/// </summary>
|
|||
/// <param name="image">The image to provide pixel access for.</param>
|
|||
public PixelAccessor(ImageBase<Color> image) |
|||
: base(image) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void CopyFromXyzw(PixelArea<Color> area, int targetX, int targetY, int width, int height) |
|||
{ |
|||
uint byteCount = (uint)width * 4; |
|||
|
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
byte* source = area.PixelBase + (y * area.RowStride); |
|||
byte* destination = this.GetRowPointer(targetX, targetY + y); |
|||
|
|||
Unsafe.CopyBlock(destination, source, byteCount); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void CopyFromXyz(PixelArea<Color> area, int targetX, int targetY, int width, int height) |
|||
{ |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
byte* source = area.PixelBase + (y * area.RowStride); |
|||
byte* destination = this.GetRowPointer(targetX, targetY + y); |
|||
|
|||
for (int x = 0; x < width; x++) |
|||
{ |
|||
Unsafe.Write(destination, (uint)(*source << 0 | *(source + 1) << 8 | *(source + 2) << 16 | 255 << 24)); |
|||
|
|||
source += 3; |
|||
destination += 4; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void CopyFromZyx(PixelArea<Color> area, int targetX, int targetY, int width, int height) |
|||
{ |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
byte* source = area.PixelBase + (y * area.RowStride); |
|||
byte* destination = this.GetRowPointer(targetX, targetY + y); |
|||
|
|||
for (int x = 0; x < width; x++) |
|||
{ |
|||
Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | 255 << 24)); |
|||
|
|||
source += 3; |
|||
destination += 4; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void CopyFromZyxw(PixelArea<Color> area, int targetX, int targetY, int width, int height) |
|||
{ |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
byte* source = area.PixelBase + (y * area.RowStride); |
|||
byte* destination = this.GetRowPointer(targetX, targetY + y); |
|||
|
|||
for (int x = 0; x < width; x++) |
|||
{ |
|||
Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | *(source + 3) << 24)); |
|||
|
|||
source += 4; |
|||
destination += 4; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void CopyToZyx(PixelArea<Color> area, int sourceX, int sourceY, int width, int height) |
|||
{ |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
byte* source = this.GetRowPointer(sourceX, sourceY + y); |
|||
byte* destination = area.PixelBase + (y * area.RowStride); |
|||
|
|||
for (int x = 0; x < width; x++) |
|||
{ |
|||
*destination = *(source + 2); |
|||
*(destination + 1) = *(source + 1); |
|||
*(destination + 2) = *(source + 0); |
|||
|
|||
source += 4; |
|||
destination += 3; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void CopyToXyz(PixelArea<Color> area, int sourceX, int sourceY, int width, int height) |
|||
{ |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
byte* source = this.GetRowPointer(sourceX, sourceY + y); |
|||
byte* destination = area.PixelBase + (y * area.RowStride); |
|||
|
|||
for (int x = 0; x < width; x++) |
|||
{ |
|||
*destination = *(source + 0); |
|||
*(destination + 1) = *(source + 1); |
|||
*(destination + 2) = *(source + 2); |
|||
|
|||
source += 4; |
|||
destination += 3; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void CopyToZyxw(PixelArea<Color> area, int sourceX, int sourceY, int width, int height) |
|||
{ |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
byte* source = this.GetRowPointer(sourceX, sourceY + y); |
|||
byte* destination = area.PixelBase + (y * area.RowStride); |
|||
|
|||
for (int x = 0; x < width; x++) |
|||
{ |
|||
*destination = *(source + 2); |
|||
*(destination + 1) = *(source + 1); |
|||
*(destination + 2) = *(source + 0); |
|||
*(destination + 3) = *(source + 3); |
|||
|
|||
source += 4; |
|||
destination += 4; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
// ReSharper disable InconsistentNaming
|
|||
namespace ImageSharp.Benchmarks.Color.Bulk |
|||
{ |
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using Color = ImageSharp.Color; |
|||
|
|||
public abstract class PackFromXyzw<TColor> |
|||
where TColor : struct, IPixel<TColor> |
|||
{ |
|||
private PinnedBuffer<TColor> destination; |
|||
|
|||
private PinnedBuffer<byte> source; |
|||
|
|||
[Params(16, 128, 1024)] |
|||
public int Count { get; set; } |
|||
|
|||
[Setup] |
|||
public void Setup() |
|||
{ |
|||
this.destination = new PinnedBuffer<TColor>(this.Count); |
|||
this.source = new PinnedBuffer<byte>(this.Count * 4); |
|||
} |
|||
|
|||
[Cleanup] |
|||
public void Cleanup() |
|||
{ |
|||
this.destination.Dispose(); |
|||
this.source.Dispose(); |
|||
} |
|||
|
|||
[Benchmark(Baseline = true)] |
|||
public void PerElement() |
|||
{ |
|||
byte[] s = this.source.Array; |
|||
TColor[] d = this.destination.Array; |
|||
|
|||
for (int i = 0; i < this.Count; i++) |
|||
{ |
|||
int i4 = i * 4; |
|||
TColor c = default(TColor); |
|||
c.PackFromBytes(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3]); |
|||
d[i] = c; |
|||
} |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void CommonBulk() |
|||
{ |
|||
new BulkPixelOperations<TColor>().PackFromXyzwBytes(this.source, this.destination, this.Count); |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void OptimizedBulk() |
|||
{ |
|||
BulkPixelOperations<TColor>.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count); |
|||
} |
|||
} |
|||
|
|||
public class PackFromXyzw_Color : PackFromXyzw<Color> |
|||
{ |
|||
} |
|||
} |
|||
@ -1,129 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace ImageSharp.Benchmarks.Color.Bulk |
|||
{ |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using Color = ImageSharp.Color; |
|||
|
|||
/// <summary>
|
|||
/// Benchmark to measure the effect of using virtual bulk-copy calls inside PixelAccessor methods
|
|||
/// </summary>
|
|||
public unsafe class PixelAccessorVirtualCopy |
|||
{ |
|||
abstract class CopyExecutor |
|||
{ |
|||
internal abstract void VirtualCopy(BufferPointer<Color> destination, BufferPointer<byte> source, int count); |
|||
} |
|||
|
|||
class UnsafeCopyExecutor : CopyExecutor |
|||
{ |
|||
[MethodImpl(MethodImplOptions.NoInlining)] |
|||
internal override unsafe void VirtualCopy(BufferPointer<Color> destination, BufferPointer<byte> source, int count) |
|||
{ |
|||
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)count*4); |
|||
} |
|||
} |
|||
|
|||
private PixelAccessor<Color> pixelAccessor; |
|||
|
|||
private PixelArea<Color> area; |
|||
|
|||
private CopyExecutor executor; |
|||
|
|||
[Params(64, 256, 512)] |
|||
public int Width { get; set; } |
|||
|
|||
public int Height { get; set; } = 256; |
|||
|
|||
|
|||
[Setup] |
|||
public void Setup() |
|||
{ |
|||
this.pixelAccessor = new PixelAccessor<ImageSharp.Color>(this.Width, this.Height); |
|||
this.area = new PixelArea<Color>(this.Width / 2, this.Height, ComponentOrder.Xyzw); |
|||
this.executor = new UnsafeCopyExecutor(); |
|||
} |
|||
|
|||
[Cleanup] |
|||
public void Cleanup() |
|||
{ |
|||
this.pixelAccessor.Dispose(); |
|||
this.area.Dispose(); |
|||
} |
|||
|
|||
[Benchmark(Baseline = true)] |
|||
public void CopyRawUnsafeInlined() |
|||
{ |
|||
uint byteCount = (uint)this.area.Width * 4; |
|||
|
|||
int targetX = this.Width / 4; |
|||
int targetY = 0; |
|||
|
|||
for (int y = 0; y < this.Height; y++) |
|||
{ |
|||
byte* source = this.area.PixelBase + (y * this.area.RowStride); |
|||
byte* destination = this.GetRowPointer(targetX, targetY + y); |
|||
|
|||
Unsafe.CopyBlock(destination, source, byteCount); |
|||
} |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void CopyBufferPointerUnsafeInlined() |
|||
{ |
|||
uint byteCount = (uint)this.area.Width * 4; |
|||
|
|||
int targetX = this.Width / 4; |
|||
int targetY = 0; |
|||
|
|||
for (int y = 0; y < this.Height; y++) |
|||
{ |
|||
BufferPointer<byte> source = this.GetAreaRow(y); |
|||
BufferPointer<Color> destination = this.GetPixelAccessorRow(targetX, targetY + y); |
|||
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount); |
|||
} |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void CopyBufferPointerUnsafeVirtual() |
|||
{ |
|||
int targetX = this.Width / 4; |
|||
int targetY = 0; |
|||
|
|||
for (int y = 0; y < this.Height; y++) |
|||
{ |
|||
BufferPointer<byte> source = this.GetAreaRow(y); |
|||
BufferPointer<Color> destination = this.GetPixelAccessorRow(targetX, targetY + y); |
|||
this.executor.VirtualCopy(destination, source, this.area.Width); |
|||
} |
|||
} |
|||
|
|||
private byte* GetRowPointer(int x, int y) |
|||
{ |
|||
return (byte*)this.pixelAccessor.DataPointer + (((y * this.pixelAccessor.Width) + x) * Unsafe.SizeOf<Color>()); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private BufferPointer<Color> GetPixelAccessorRow(int x, int y) |
|||
{ |
|||
return new BufferPointer<ImageSharp.Color>( |
|||
this.pixelAccessor.PixelBuffer, |
|||
(void*)this.pixelAccessor.DataPointer, |
|||
(y * this.pixelAccessor.Width) + x |
|||
); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private BufferPointer<byte> GetAreaRow(int y) |
|||
{ |
|||
return new BufferPointer<byte>(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
// ReSharper disable InconsistentNaming
|
|||
namespace ImageSharp.Benchmarks.Color.Bulk |
|||
{ |
|||
using System.Numerics; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
public abstract class ToVector4<TColor> |
|||
where TColor : struct, IPixel<TColor> |
|||
{ |
|||
private PinnedBuffer<TColor> source; |
|||
|
|||
private PinnedBuffer<Vector4> destination; |
|||
|
|||
[Params(64, 300, 1024)] |
|||
public int Count { get; set; } |
|||
|
|||
[Setup] |
|||
public void Setup() |
|||
{ |
|||
this.source = new PinnedBuffer<TColor>(this.Count); |
|||
this.destination = new PinnedBuffer<Vector4>(this.Count); |
|||
} |
|||
|
|||
[Cleanup] |
|||
public void Cleanup() |
|||
{ |
|||
this.source.Dispose(); |
|||
this.destination.Dispose(); |
|||
} |
|||
|
|||
[Benchmark(Baseline = true)] |
|||
public void PerElement() |
|||
{ |
|||
TColor[] s = this.source.Array; |
|||
Vector4[] d = this.destination.Array; |
|||
|
|||
for (int i = 0; i < this.Count; i++) |
|||
{ |
|||
TColor c = s[i]; |
|||
d[i] = c.ToVector4(); |
|||
} |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void CommonBulk() |
|||
{ |
|||
new BulkPixelOperations<TColor>().ToVector4(this.source, this.destination, this.Count); |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void OptimizedBulk() |
|||
{ |
|||
BulkPixelOperations<TColor>.Instance.ToVector4(this.source, this.destination, this.Count); |
|||
} |
|||
} |
|||
|
|||
public class ToVector4_Color : ToVector4<ImageSharp.Color> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
// ReSharper disable InconsistentNaming
|
|||
namespace ImageSharp.Benchmarks.Color.Bulk |
|||
{ |
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using Color = ImageSharp.Color; |
|||
|
|||
public abstract class ToXyz<TColor> |
|||
where TColor : struct, IPixel<TColor> |
|||
{ |
|||
private PinnedBuffer<TColor> source; |
|||
|
|||
private PinnedBuffer<byte> destination; |
|||
|
|||
[Params(16, 128, 1024)] |
|||
public int Count { get; set; } |
|||
|
|||
[Setup] |
|||
public void Setup() |
|||
{ |
|||
this.source = new PinnedBuffer<TColor>(this.Count); |
|||
this.destination = new PinnedBuffer<byte>(this.Count * 3); |
|||
} |
|||
|
|||
[Cleanup] |
|||
public void Cleanup() |
|||
{ |
|||
this.source.Dispose(); |
|||
this.destination.Dispose(); |
|||
} |
|||
|
|||
[Benchmark(Baseline = true)] |
|||
public void PerElement() |
|||
{ |
|||
TColor[] s = this.source.Array; |
|||
byte[] d = this.destination.Array; |
|||
|
|||
for (int i = 0; i < this.Count; i++) |
|||
{ |
|||
TColor c = s[i]; |
|||
c.ToXyzBytes(d, i * 4); |
|||
} |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void CommonBulk() |
|||
{ |
|||
new BulkPixelOperations<TColor>().ToXyzBytes(this.source, this.destination, this.Count); |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void OptimizedBulk() |
|||
{ |
|||
BulkPixelOperations<TColor>.Instance.ToXyzBytes(this.source, this.destination, this.Count); |
|||
} |
|||
} |
|||
|
|||
public class ToXyz_Color : ToXyz<Color> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
// ReSharper disable InconsistentNaming
|
|||
|
|||
namespace ImageSharp.Benchmarks.Color.Bulk |
|||
{ |
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using Color = ImageSharp.Color; |
|||
|
|||
public abstract class ToXyzw<TColor> |
|||
where TColor : struct, IPixel<TColor> |
|||
{ |
|||
private PinnedBuffer<TColor> source; |
|||
|
|||
private PinnedBuffer<byte> destination; |
|||
|
|||
[Params(16, 128, 1024)] |
|||
public int Count { get; set; } |
|||
|
|||
[Setup] |
|||
public void Setup() |
|||
{ |
|||
this.source = new PinnedBuffer<TColor>(this.Count); |
|||
this.destination = new PinnedBuffer<byte>(this.Count * 4); |
|||
} |
|||
|
|||
[Cleanup] |
|||
public void Cleanup() |
|||
{ |
|||
this.source.Dispose(); |
|||
this.destination.Dispose(); |
|||
} |
|||
|
|||
[Benchmark(Baseline = true)] |
|||
public void PerElement() |
|||
{ |
|||
TColor[] s = this.source.Array; |
|||
byte[] d = this.destination.Array; |
|||
|
|||
for (int i = 0; i < this.Count; i++) |
|||
{ |
|||
TColor c = s[i]; |
|||
c.ToXyzwBytes(d, i * 4); |
|||
} |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void CommonBulk() |
|||
{ |
|||
new BulkPixelOperations<TColor>().ToXyzwBytes(this.source, this.destination, this.Count); |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void OptimizedBulk() |
|||
{ |
|||
BulkPixelOperations<TColor>.Instance.ToXyzwBytes(this.source, this.destination, this.Count); |
|||
} |
|||
} |
|||
|
|||
public class ToXyzw_Color : ToXyzw<Color> |
|||
{ |
|||
} |
|||
|
|||
public class ToXyzw_Argb : ToXyzw<Argb> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// ReSharper disable InconsistentNaming
|
|||
namespace ImageSharp.Benchmarks.General |
|||
{ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using Color = ImageSharp.Color; |
|||
|
|||
public unsafe class ClearBuffer |
|||
{ |
|||
private PinnedBuffer<Color> buffer; |
|||
|
|||
[Params(32, 128, 512)] |
|||
public int Count { get; set; } |
|||
|
|||
[Setup] |
|||
public void Setup() |
|||
{ |
|||
this.buffer = new PinnedBuffer<ImageSharp.Color>(this.Count); |
|||
} |
|||
|
|||
[Cleanup] |
|||
public void Cleanup() |
|||
{ |
|||
this.buffer.Dispose(); |
|||
} |
|||
|
|||
[Benchmark(Baseline = true)] |
|||
public void Array_Clear() |
|||
{ |
|||
Array.Clear(this.buffer.Array, 0, this.Count); |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void Unsafe_InitBlock() |
|||
{ |
|||
Unsafe.InitBlock((void*)this.buffer.Pointer, default(byte), (uint)this.Count*sizeof(uint)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Numerics; |
|||
|
|||
internal struct ApproximateFloatComparer : IEqualityComparer<float>, IEqualityComparer<Vector4> |
|||
{ |
|||
private readonly float Eps; |
|||
|
|||
public ApproximateFloatComparer(float eps = 1f) |
|||
{ |
|||
this.Eps = eps; |
|||
} |
|||
|
|||
public bool Equals(float x, float y) |
|||
{ |
|||
float d = x - y; |
|||
|
|||
return d > -this.Eps && d < this.Eps; |
|||
} |
|||
|
|||
public int GetHashCode(float obj) |
|||
{ |
|||
throw new InvalidOperationException(); |
|||
} |
|||
|
|||
public bool Equals(Vector4 a, Vector4 b) |
|||
{ |
|||
return this.Equals(a.X, b.X) && this.Equals(a.Y, b.Y) && this.Equals(a.Z, b.Z) && this.Equals(a.W, b.W); |
|||
} |
|||
|
|||
public int GetHashCode(Vector4 obj) |
|||
{ |
|||
throw new InvalidOperationException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
Title: Home |
|||
--- |
|||
Welcome to the documentation for ImageSharp |
|||
Loading…
Reference in new issue