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