diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs
index 2cf18b2456..43eebeac87 100644
--- a/src/ImageSharp/Common/Helpers/DebugGuard.cs
+++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs
@@ -163,6 +163,20 @@ namespace SixLabors.ImageSharp
}
}
+ ///
+ /// Verifies whether a specific condition is met, throwing an exception if it's false.
+ ///
+ /// The condition
+ /// The error message
+ [Conditional("DEBUG")]
+ public static void IsTrue(bool target, string message)
+ {
+ if (!target)
+ {
+ throw new InvalidOperationException(message);
+ }
+ }
+
///
/// Verifies, that the method parameter with specified target value is false
/// and throws an exception if it is found to be so.
diff --git a/src/ImageSharp/Common/Helpers/InliningOptions.cs b/src/ImageSharp/Common/Helpers/InliningOptions.cs
index ad85c4fc81..069a426d75 100644
--- a/src/ImageSharp/Common/Helpers/InliningOptions.cs
+++ b/src/ImageSharp/Common/Helpers/InliningOptions.cs
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-// Uncomment this for verbose profiler results:
+// Uncomment this for verbose profiler results. DO NOT PUSH TO MAIN!
// #define PROFILING
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
index 2ac577264c..9aeb209319 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
@@ -25,6 +25,20 @@ namespace SixLabors.ImageSharp
false;
#endif
+ ///
+ /// Widen and convert a vector of values into 2 vectors of -s.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static void ConvertToSingle(
+ Vector source,
+ out Vector dest1,
+ out Vector dest2)
+ {
+ Vector.Widen(source, out Vector i1, out Vector i2);
+ dest1 = Vector.ConvertToSingle(i1);
+ dest2 = Vector.ConvertToSingle(i2);
+ }
+
///
/// as many elements as possible, slicing them down (keeping the remainder).
///
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index d7e2e40e29..17ee616072 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void EncodeGlobal(Image image, QuantizedFrame quantized, int transparencyIndex, Stream stream)
where TPixel : struct, IPixel
{
- var palleteQuantizer = new PaletteQuantizer(this.quantizer.Diffuser);
+ var palleteQuantizer = new PaletteQuantizer(quantized.Palette, this.quantizer.Diffuser);
for (int i = 0; i < image.Frames.Count; i++)
{
@@ -158,8 +158,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
else
{
- using (QuantizedFrame paletteQuantized
- = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration(), () => quantized.Palette).QuantizeFrame(frame))
+ using (QuantizedFrame paletteQuantized = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration()).QuantizeFrame(frame))
{
this.WriteImageData(paletteQuantized, stream);
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
index b7dd125a88..6bf9c8483a 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
@@ -3,64 +3,41 @@
using System.Numerics;
using System.Runtime.CompilerServices;
-
using SixLabors.ImageSharp.Memory;
+// ReSharper disable UseObjectOrCollectionInitializer
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
///
- /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical.
+ /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical scale factors.
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(in BufferArea area, int horizontalScale, int verticalScale)
{
if (horizontalScale == 1 && verticalScale == 1)
{
- this.CopyTo(area);
+ this.Copy1x1Scale(area);
return;
}
- else if (horizontalScale == 2 && verticalScale == 2)
+
+ if (horizontalScale == 2 && verticalScale == 2)
{
- this.CopyTo2x2(area);
+ this.Copy2x2Scale(area);
return;
}
- ref float destBase = ref area.GetReferenceToOrigin();
-
- // TODO: Optimize: implement all the cases with loopless special code! (T4?)
- for (int y = 0; y < 8; y++)
- {
- int yy = y * verticalScale;
- int y8 = y * 8;
-
- for (int x = 0; x < 8; x++)
- {
- int xx = x * horizontalScale;
-
- float value = this[y8 + x];
-
- for (int i = 0; i < verticalScale; i++)
- {
- int baseIdx = ((yy + i) * area.Stride) + xx;
-
- for (int j = 0; j < horizontalScale; j++)
- {
- // area[xx + j, yy + i] = value;
- Unsafe.Add(ref destBase, baseIdx + j) = value;
- }
- }
- }
- }
+ // TODO: Optimize: implement all cases with scale-specific, loopless code!
+ this.CopyArbitraryScale(area, horizontalScale, verticalScale);
}
- // [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void CopyTo(in BufferArea area)
+ public void Copy1x1Scale(in BufferArea destination)
{
ref byte selfBase = ref Unsafe.As(ref this);
- ref byte destBase = ref Unsafe.As(ref area.GetReferenceToOrigin());
- int destStride = area.Stride * sizeof(float);
+ ref byte destBase = ref Unsafe.As(ref destination.GetReferenceToOrigin());
+ int destStride = destination.Stride * sizeof(float);
CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
CopyRowImpl(ref selfBase, ref destBase, destStride, 1);
@@ -80,76 +57,86 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float));
}
- private void CopyTo2x2(in BufferArea area)
+ private void Copy2x2Scale(in BufferArea area)
{
- ref float destBase = ref area.GetReferenceToOrigin();
- int destStride = area.Stride;
-
- this.WidenCopyImpl2x2(ref destBase, 0, destStride);
- this.WidenCopyImpl2x2(ref destBase, 1, destStride);
- this.WidenCopyImpl2x2(ref destBase, 2, destStride);
- this.WidenCopyImpl2x2(ref destBase, 3, destStride);
- this.WidenCopyImpl2x2(ref destBase, 4, destStride);
- this.WidenCopyImpl2x2(ref destBase, 5, destStride);
- this.WidenCopyImpl2x2(ref destBase, 6, destStride);
- this.WidenCopyImpl2x2(ref destBase, 7, destStride);
+ ref Vector2 destBase = ref Unsafe.As(ref area.GetReferenceToOrigin());
+ int destStride = area.Stride / 2;
+
+ this.WidenCopyRowImpl2x2(ref destBase, 0, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 1, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 2, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 3, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 4, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 5, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 6, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WidenCopyImpl2x2(ref float destBase, int row, int destStride)
+ private void WidenCopyRowImpl2x2(ref Vector2 destBase, int row, int destStride)
{
- ref Vector4 selfLeft = ref Unsafe.Add(ref this.V0L, 2 * row);
- ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1);
- ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride);
-
- Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X;
- Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X;
- Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y;
- Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y;
- Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z;
- Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z;
- Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W;
- Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W;
-
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W;
-
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W;
-
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W;
+ ref Vector4 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row);
+ ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
+
+ int offset = 2 * row * destStride;
+ ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset));
+ ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride));
+
+ var xyLeft = new Vector4(sLeft.X);
+ xyLeft.Z = sLeft.Y;
+ xyLeft.W = sLeft.Y;
+
+ var zwLeft = new Vector4(sLeft.Z);
+ zwLeft.Z = sLeft.W;
+ zwLeft.W = sLeft.W;
+
+ var xyRight = new Vector4(sRight.X);
+ xyRight.Z = sRight.Y;
+ xyRight.W = sRight.Y;
+
+ var zwRight = new Vector4(sRight.Z);
+ zwRight.Z = sRight.W;
+ zwRight.W = sRight.W;
+
+ dTopLeft = xyLeft;
+ Unsafe.Add(ref dTopLeft, 1) = zwLeft;
+ Unsafe.Add(ref dTopLeft, 2) = xyRight;
+ Unsafe.Add(ref dTopLeft, 3) = zwRight;
+
+ dBottomLeft = xyLeft;
+ Unsafe.Add(ref dBottomLeft, 1) = zwLeft;
+ Unsafe.Add(ref dBottomLeft, 2) = xyRight;
+ Unsafe.Add(ref dBottomLeft, 3) = zwRight;
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WidenCopyImpl(ref Vector4 s, ref float destBase)
+ [MethodImpl(InliningOptions.ColdPath)]
+ private void CopyArbitraryScale(BufferArea area, int horizontalScale, int verticalScale)
{
- Unsafe.Add(ref destBase, 0) = s.X;
- Unsafe.Add(ref destBase, 1) = s.X;
- Unsafe.Add(ref destBase, 2) = s.Y;
- Unsafe.Add(ref destBase, 3) = s.Y;
- Unsafe.Add(ref destBase, 4) = s.Z;
- Unsafe.Add(ref destBase, 5) = s.Z;
- Unsafe.Add(ref destBase, 6) = s.W;
- Unsafe.Add(ref destBase, 7) = s.W;
+ ref float destBase = ref area.GetReferenceToOrigin();
+
+ for (int y = 0; y < 8; y++)
+ {
+ int yy = y * verticalScale;
+ int y8 = y * 8;
+
+ for (int x = 0; x < 8; x++)
+ {
+ int xx = x * horizontalScale;
+
+ float value = this[y8 + x];
+
+ for (int i = 0; i < verticalScale; i++)
+ {
+ int baseIdx = ((yy + i) * area.Stride) + xx;
+
+ for (int j = 0; j < horizontalScale; j++)
+ {
+ // area[xx + j, yy + i] = value;
+ Unsafe.Add(ref destBase, baseIdx + j) = value;
+ }
+ }
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
index e83896f587..09ed6408d7 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Transpose the block into the destination block.
///
/// The destination block
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInto(ref Block8x8F d)
{
d.V0L.X = V0L.X;
@@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// AVX2-only variant for executing and in one step.
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2()
{
Vector off = new Vector(128f);
@@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Fill the block from 'source' doing short -> float conversion.
///
- public void LoadFrom(ref Block8x8 source)
+ public void LoadFromInt16Scalar(ref Block8x8 source)
{
ref short selfRef = ref Unsafe.As(ref source);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
index 82d82ef0c2..f93ee6522d 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
@@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Transpose the block into the destination block.
///
/// The destination block
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInto(ref Block8x8F d)
{
<#
@@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// AVX2-only variant for executing and in one step.
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2()
{
Vector off = new Vector(128f);
@@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Fill the block from 'source' doing short -> float conversion.
///
- public void LoadFrom(ref Block8x8 source)
+ public void LoadFromInt16Scalar(ref Block8x8 source)
{
ref short selfRef = ref Unsafe.As(ref source);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index 59fc234c42..137a8029d8 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// The float value at the specified index
public float this[int idx]
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
get
{
GuardBlockIndex(idx);
@@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return Unsafe.Add(ref selfRef, idx);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
set
{
GuardBlockIndex(idx);
@@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Fill the block with defaults (zeroes)
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void Clear()
{
// The cheapest way to do this in C#:
@@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Load raw 32bit floating point data from source
///
/// Source
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void LoadFrom(Span source)
{
ref byte s = ref Unsafe.As(ref MemoryMarshal.GetReference(source));
@@ -174,7 +174,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Block pointer
/// Source
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void LoadFrom(Block8x8F* blockPtr, Span source)
{
blockPtr->LoadFrom(source);
@@ -200,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Copy raw 32bit floating point data to dest
///
/// Destination
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(Span dest)
{
ref byte d = ref Unsafe.As(ref MemoryMarshal.GetReference(dest));
@@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Pointer to block
/// Destination
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest)
{
float* fPtr = (float*)blockPtr;
@@ -230,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Block pointer
/// Destination
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest)
{
blockPtr->CopyTo(dest);
@@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Copy raw 32bit floating point data to dest
///
/// Destination
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public unsafe void CopyTo(float[] dest)
{
fixed (void* ptr = &this.V0L)
@@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Multiply all elements of the block.
///
/// The value to multiply by
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInplace(float value)
{
this.V0L *= value;
@@ -300,7 +300,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Multiply all elements of the block by the corresponding elements of 'other'
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInplace(ref Block8x8F other)
{
this.V0L *= other.V0L;
@@ -325,7 +325,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Adds a vector to all elements of the block.
///
/// The added vector
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void AddToAllInplace(Vector4 diff)
{
this.V0L += diff;
@@ -420,7 +420,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b)
{
a.V0L = DivideRound(a.V0L, b.V0L);
@@ -493,6 +493,51 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void LoadFrom(ref Block8x8 source)
+ {
+#if SUPPORTS_EXTENDED_INTRINSICS
+ if (SimdUtils.IsAvx2CompatibleArchitecture)
+ {
+ this.LoadFromInt16ExtendedAvx2(ref source);
+ return;
+ }
+#endif
+ this.LoadFromInt16Scalar(ref source);
+ }
+
+ ///
+ /// Loads values from using extended AVX2 intrinsics.
+ ///
+ /// The source
+ public void LoadFromInt16ExtendedAvx2(ref Block8x8 source)
+ {
+ DebugGuard.IsTrue(
+ SimdUtils.IsAvx2CompatibleArchitecture,
+ "LoadFromUInt16ExtendedAvx2 only works on AVX2 compatible architecture!");
+
+ ref Vector sRef = ref Unsafe.As>(ref source);
+ ref Vector dRef = ref Unsafe.As>(ref this);
+
+ // Vector.Count == 16 on AVX2
+ // We can process 2 block rows in a single step
+ SimdUtils.ExtendedIntrinsics.ConvertToSingle(sRef, out Vector top, out Vector bottom);
+ dRef = top;
+ Unsafe.Add(ref dRef, 1) = bottom;
+
+ SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 1), out top, out bottom);
+ Unsafe.Add(ref dRef, 2) = top;
+ Unsafe.Add(ref dRef, 3) = bottom;
+
+ SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 2), out top, out bottom);
+ Unsafe.Add(ref dRef, 4) = top;
+ Unsafe.Add(ref dRef, 5) = bottom;
+
+ SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 3), out top, out bottom);
+ Unsafe.Add(ref dRef, 6) = top;
+ Unsafe.Add(ref dRef, 7) = bottom;
+ }
+
///
public override string ToString()
{
@@ -511,7 +556,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return sb.ToString();
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private static Vector NormalizeAndRound(Vector row, Vector off, Vector max)
{
row += off;
@@ -520,7 +565,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return row.FastRound();
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{
// sign(dividend) = max(min(dividend, 1), -1)
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
index 5d7a31a12b..7424145c3b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
@@ -3,6 +3,8 @@
using System;
using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
@@ -17,24 +19,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
public override void ConvertToRgba(in ComponentValues values, Span result)
{
- // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()!
- ReadOnlySpan yVals = values.Component0;
-
- var v = new Vector4(0, 0, 0, 1);
-
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
+ ref float sBase = ref MemoryMarshal.GetReference(values.Component0);
+ ref Vector4 dBase = ref MemoryMarshal.GetReference(result);
+
for (int i = 0; i < result.Length; i++)
{
- float y = yVals[i];
-
- v.X = y;
- v.Y = y;
- v.Z = y;
-
+ var v = new Vector4(Unsafe.Add(ref sBase, i));
+ v.W = 1f;
v *= scale;
-
- result[i] = v;
+ Unsafe.Add(ref dBase, i) = v;
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
index c033980336..2492a985a8 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Memory;
-using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@@ -43,16 +42,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Gets the storing the "raw" frequency-domain decoded + unzigged blocks.
- /// We need to apply IDCT and dequantiazition to transform them into color-space blocks.
+ /// We need to apply IDCT and dequantization to transform them into color-space blocks.
///
Buffer2D SpectralBlocks { get; }
-
- ///
- /// Gets a reference to the at the given row and column index from
- ///
- /// The column
- /// The row
- /// The
- ref Block8x8 GetBlockReference(int column, int row);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
index 65a584c4f2..ef03582d69 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
@@ -128,21 +128,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
}
- this.SpectralBlocks = this.memoryAllocator.Allocate2D(blocksPerColumnForMcu, blocksPerLineForMcu + 1, AllocationOptions.Clean);
- }
+ int totalNumberOfBlocks = blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
+ int width = this.WidthInBlocks + 1;
+ int height = totalNumberOfBlocks / width;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ref Block8x8 GetBlockReference(int column, int row)
- {
- int offset = ((this.WidthInBlocks + 1) * row) + column;
- return ref Unsafe.Add(ref MemoryMarshal.GetReference(this.SpectralBlocks.GetSpan()), offset);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ref short GetBlockDataReference(int column, int row)
- {
- ref Block8x8 blockRef = ref this.GetBlockReference(column, row);
- return ref Unsafe.As(ref blockRef);
+ this.SpectralBlocks = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
index 890f402595..57a53549f5 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
@@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
@@ -88,12 +90,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int yBuffer = y * this.blockAreaSize.Height;
- for (int x = 0; x < this.SizeInBlocks.Width; x++)
- {
- int xBlock = x;
- int xBuffer = x * this.blockAreaSize.Width;
+ Span blockRow = this.Component.SpectralBlocks.GetRowSpan(yBlock);
+
+ ref Block8x8 blockRowBase = ref MemoryMarshal.GetReference(blockRow);
- ref Block8x8 block = ref this.Component.GetBlockReference(xBlock, yBlock);
+ for (int xBlock = 0; xBlock < this.SizeInBlocks.Width; xBlock++)
+ {
+ ref Block8x8 block = ref Unsafe.Add(ref blockRowBase, xBlock);
+ int xBuffer = xBlock * this.blockAreaSize.Width;
BufferArea destArea = this.ColorBuffer.GetArea(
xBuffer,
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
index 351e453484..5d232b5713 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
@@ -1,8 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.IO;
+using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
@@ -142,7 +144,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private static uint LRot(uint x, int y) => (x << y) | (x >> (32 - y));
private void ParseBaselineData()
@@ -179,10 +181,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
+ int mcuRow = mcu / mcusPerLine;
+
// Scan out an mcu's worth of this component; that's just determined
// by the basic H and V specified for the component
for (int y = 0; y < v; y++)
{
+ int blockRow = (mcuRow * v) + y;
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
for (int x = 0; x < h; x++)
{
if (this.eof)
@@ -190,15 +196,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
- int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockBaseline(
component,
- blockRow,
- blockCol,
+ ref blockSpan[blockCol],
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
@@ -236,6 +239,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0;
for (int j = 0; j < h; j++)
{
+ // TODO: Isn't blockRow == j actually?
+ int blockRow = mcu / w;
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
+
for (int i = 0; i < w; i++)
{
if (this.eof)
@@ -243,13 +250,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- int blockRow = mcu / w;
+ // TODO: Isn't blockCol == i actually?
int blockCol = mcu % w;
this.DecodeBlockBaseline(
component,
- blockRow,
- blockCol,
+ ref blockSpan[blockCol],
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
@@ -299,6 +305,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// by the basic H and V specified for the component
for (int y = 0; y < v; y++)
{
+ int mcuRow = mcu / mcusPerLine;
+ int blockRow = (mcuRow * v) + y;
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
+
for (int x = 0; x < h; x++)
{
if (this.eof)
@@ -306,15 +316,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
- int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockProgressiveDC(
component,
- blockRow,
- blockCol,
+ ref blockSpan[blockCol],
ref dcHuffmanTable);
}
}
@@ -351,6 +358,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0;
for (int j = 0; j < h; j++)
{
+ // TODO: isn't blockRow == j actually?
+ int blockRow = mcu / w;
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
+
for (int i = 0; i < w; i++)
{
if (this.eof)
@@ -358,23 +369,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- int blockRow = mcu / w;
+ // TODO: isn't blockCol == i actually?
int blockCol = mcu % w;
+ ref Block8x8 block = ref blockSpan[blockCol];
+
if (this.spectralStart == 0)
{
this.DecodeBlockProgressiveDC(
component,
- blockRow,
- blockCol,
+ ref block,
ref dcHuffmanTable);
}
else
{
this.DecodeBlockProgressiveAC(
- component,
- blockRow,
- blockCol,
+ ref block,
ref acHuffmanTable,
ref fastACRef);
}
@@ -391,8 +401,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockBaseline(
JpegComponent component,
- int row,
- int col,
+ ref Block8x8 block,
ref HuffmanTable dcTable,
ref HuffmanTable acTable,
ref short fastACRef)
@@ -405,7 +414,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
JpegThrowHelper.ThrowBadHuffmanCode();
}
- ref short blockDataRef = ref component.GetBlockDataReference(col, row);
+ ref short blockDataRef = ref Unsafe.As(ref block);
int diff = t != 0 ? this.ExtendReceive(t) : 0;
int dc = component.DcPredictor + diff;
@@ -470,8 +479,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockProgressiveDC(
JpegComponent component,
- int row,
- int col,
+ ref Block8x8 block,
ref HuffmanTable dcTable)
{
if (this.spectralEnd != 0)
@@ -481,7 +489,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.CheckBits();
- ref short blockDataRef = ref component.GetBlockDataReference(col, row);
+ ref short blockDataRef = ref Unsafe.As(ref block);
if (this.successiveHigh == 0)
{
@@ -505,9 +513,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
private void DecodeBlockProgressiveAC(
- JpegComponent component,
- int row,
- int col,
+ ref Block8x8 block,
ref HuffmanTable acTable,
ref short fastACRef)
{
@@ -516,7 +522,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC.");
}
- ref short blockDataRef = ref component.GetBlockDataReference(col, row);
+ ref short blockDataRef = ref Unsafe.As(ref block);
if (this.successiveHigh == 0)
{
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index 36246c6820..ef73aab38f 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -913,7 +913,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The table index
/// The codelengths
/// The values
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values)
=> tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values);
@@ -921,7 +921,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// Reads a from the stream advancing it by two bytes
///
/// The
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private ushort ReadUint16()
{
this.InputStream.Read(this.markerBuffer, 0, 2);
@@ -936,12 +936,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private Image PostProcessIntoImage()
where TPixel : struct, IPixel
{
+ var image = Image.CreateUninitialized(
+ this.configuration,
+ this.ImageWidth,
+ this.ImageHeight,
+ this.MetaData);
+
using (var postProcessor = new JpegImagePostProcessor(this.configuration, this))
{
- var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
postProcessor.PostProcess(image.Frames.RootFrame);
- return image;
}
+
+ return image;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs
index 8b9f3fdb5b..ffdab25e24 100644
--- a/src/ImageSharp/Image.Decode.cs
+++ b/src/ImageSharp/Image.Decode.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@@ -15,6 +16,29 @@ namespace SixLabors.ImageSharp
///
public static partial class Image
{
+ ///
+ /// Creates an instance backed by an uninitialized memory buffer.
+ /// This is an optimized creation method intended to be used by decoders.
+ /// The image might be filled with memory garbage.
+ ///
+ /// The pixel type
+ /// The
+ /// The width of the image
+ /// The height of the image
+ /// The
+ /// The result
+ internal static Image CreateUninitialized(
+ Configuration configuration,
+ int width,
+ int height,
+ ImageMetaData metadata)
+ where TPixel : struct, IPixel
+ {
+ Buffer2D uninitializedMemoryBuffer =
+ configuration.MemoryAllocator.Allocate2D(width, height);
+ return new Image(configuration, uninitializedMemoryBuffer.MemorySource, width, height, metadata);
+ }
+
///
/// By reading the header on the provided stream this calculates the images format.
///
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
index 3326c3217a..100649c0fd 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
+++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
@@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private byte ConvertToByte(ReadOnlySpan buffer) => buffer[0];
- private unsafe string ConvertToString(ReadOnlySpan buffer)
+ private string ConvertToString(ReadOnlySpan buffer)
{
int nullCharIndex = buffer.IndexOf((byte)0);
@@ -382,13 +382,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.invalidTags.Add(tag);
}
+ [MethodImpl(InliningOptions.ShortMethod)]
private TEnum ToEnum(int value, TEnum defaultValue)
where TEnum : struct
{
- var enumValue = (TEnum)(object)value;
- if (Enum.GetValues(typeof(TEnum)).Cast().Any(v => v.Equals(enumValue)))
+ if (EnumHelper.IsDefined(value))
{
- return enumValue;
+ return Unsafe.As(ref value);
}
return defaultValue;
@@ -557,5 +557,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
? BinaryPrimitives.ReadInt16BigEndian(buffer)
: BinaryPrimitives.ReadInt16LittleEndian(buffer);
}
+
+ private class EnumHelper
+ where TEnum : struct
+ {
+ private static readonly int[] Values = Enum.GetValues(typeof(TEnum)).Cast()
+ .Select(e => Convert.ToInt32(e)).OrderBy(e => e).ToArray();
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static bool IsDefined(int value)
+ {
+ return Array.BinarySearch(Values, value) >= 0;
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/ColorConstants.cs b/src/ImageSharp/PixelFormats/ColorConstants.cs
index bac05c53d2..14df385697 100644
--- a/src/ImageSharp/PixelFormats/ColorConstants.cs
+++ b/src/ImageSharp/PixelFormats/ColorConstants.cs
@@ -11,157 +11,268 @@ namespace SixLabors.ImageSharp.PixelFormats
///
/// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4.
///
- public static readonly Rgba32[] WebSafeColors = GetWebSafeColors();
+ public static readonly Rgba32[] WebSafeColors =
+ {
+ Rgba32.AliceBlue,
+ Rgba32.AntiqueWhite,
+ Rgba32.Aqua,
+ Rgba32.Aquamarine,
+ Rgba32.Azure,
+ Rgba32.Beige,
+ Rgba32.Bisque,
+ Rgba32.Black,
+ Rgba32.BlanchedAlmond,
+ Rgba32.Blue,
+ Rgba32.BlueViolet,
+ Rgba32.Brown,
+ Rgba32.BurlyWood,
+ Rgba32.CadetBlue,
+ Rgba32.Chartreuse,
+ Rgba32.Chocolate,
+ Rgba32.Coral,
+ Rgba32.CornflowerBlue,
+ Rgba32.Cornsilk,
+ Rgba32.Crimson,
+ Rgba32.Cyan,
+ Rgba32.DarkBlue,
+ Rgba32.DarkCyan,
+ Rgba32.DarkGoldenrod,
+ Rgba32.DarkGray,
+ Rgba32.DarkGreen,
+ Rgba32.DarkKhaki,
+ Rgba32.DarkMagenta,
+ Rgba32.DarkOliveGreen,
+ Rgba32.DarkOrange,
+ Rgba32.DarkOrchid,
+ Rgba32.DarkRed,
+ Rgba32.DarkSalmon,
+ Rgba32.DarkSeaGreen,
+ Rgba32.DarkSlateBlue,
+ Rgba32.DarkSlateGray,
+ Rgba32.DarkTurquoise,
+ Rgba32.DarkViolet,
+ Rgba32.DeepPink,
+ Rgba32.DeepSkyBlue,
+ Rgba32.DimGray,
+ Rgba32.DodgerBlue,
+ Rgba32.Firebrick,
+ Rgba32.FloralWhite,
+ Rgba32.ForestGreen,
+ Rgba32.Fuchsia,
+ Rgba32.Gainsboro,
+ Rgba32.GhostWhite,
+ Rgba32.Gold,
+ Rgba32.Goldenrod,
+ Rgba32.Gray,
+ Rgba32.Green,
+ Rgba32.GreenYellow,
+ Rgba32.Honeydew,
+ Rgba32.HotPink,
+ Rgba32.IndianRed,
+ Rgba32.Indigo,
+ Rgba32.Ivory,
+ Rgba32.Khaki,
+ Rgba32.Lavender,
+ Rgba32.LavenderBlush,
+ Rgba32.LawnGreen,
+ Rgba32.LemonChiffon,
+ Rgba32.LightBlue,
+ Rgba32.LightCoral,
+ Rgba32.LightCyan,
+ Rgba32.LightGoldenrodYellow,
+ Rgba32.LightGray,
+ Rgba32.LightGreen,
+ Rgba32.LightPink,
+ Rgba32.LightSalmon,
+ Rgba32.LightSeaGreen,
+ Rgba32.LightSkyBlue,
+ Rgba32.LightSlateGray,
+ Rgba32.LightSteelBlue,
+ Rgba32.LightYellow,
+ Rgba32.Lime,
+ Rgba32.LimeGreen,
+ Rgba32.Linen,
+ Rgba32.Magenta,
+ Rgba32.Maroon,
+ Rgba32.MediumAquamarine,
+ Rgba32.MediumBlue,
+ Rgba32.MediumOrchid,
+ Rgba32.MediumPurple,
+ Rgba32.MediumSeaGreen,
+ Rgba32.MediumSlateBlue,
+ Rgba32.MediumSpringGreen,
+ Rgba32.MediumTurquoise,
+ Rgba32.MediumVioletRed,
+ Rgba32.MidnightBlue,
+ Rgba32.MintCream,
+ Rgba32.MistyRose,
+ Rgba32.Moccasin,
+ Rgba32.NavajoWhite,
+ Rgba32.Navy,
+ Rgba32.OldLace,
+ Rgba32.Olive,
+ Rgba32.OliveDrab,
+ Rgba32.Orange,
+ Rgba32.OrangeRed,
+ Rgba32.Orchid,
+ Rgba32.PaleGoldenrod,
+ Rgba32.PaleGreen,
+ Rgba32.PaleTurquoise,
+ Rgba32.PaleVioletRed,
+ Rgba32.PapayaWhip,
+ Rgba32.PeachPuff,
+ Rgba32.Peru,
+ Rgba32.Pink,
+ Rgba32.Plum,
+ Rgba32.PowderBlue,
+ Rgba32.Purple,
+ Rgba32.RebeccaPurple,
+ Rgba32.Red,
+ Rgba32.RosyBrown,
+ Rgba32.RoyalBlue,
+ Rgba32.SaddleBrown,
+ Rgba32.Salmon,
+ Rgba32.SandyBrown,
+ Rgba32.SeaGreen,
+ Rgba32.SeaShell,
+ Rgba32.Sienna,
+ Rgba32.Silver,
+ Rgba32.SkyBlue,
+ Rgba32.SlateBlue,
+ Rgba32.SlateGray,
+ Rgba32.Snow,
+ Rgba32.SpringGreen,
+ Rgba32.SteelBlue,
+ Rgba32.Tan,
+ Rgba32.Teal,
+ Rgba32.Thistle,
+ Rgba32.Tomato,
+ Rgba32.Transparent,
+ Rgba32.Turquoise,
+ Rgba32.Violet,
+ Rgba32.Wheat,
+ Rgba32.White,
+ Rgba32.WhiteSmoke,
+ Rgba32.Yellow,
+ Rgba32.YellowGreen
+ };
///
- /// Returns an array of web safe colors.
+ /// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
+ /// The hex codes were collected and defined by Nicholas Rougeux
///
- /// The
- private static Rgba32[] GetWebSafeColors()
- => new Rgba32[]
- {
- Rgba32.AliceBlue,
- Rgba32.AntiqueWhite,
- Rgba32.Aqua,
- Rgba32.Aquamarine,
- Rgba32.Azure,
- Rgba32.Beige,
- Rgba32.Bisque,
- Rgba32.Black,
- Rgba32.BlanchedAlmond,
- Rgba32.Blue,
- Rgba32.BlueViolet,
- Rgba32.Brown,
- Rgba32.BurlyWood,
- Rgba32.CadetBlue,
- Rgba32.Chartreuse,
- Rgba32.Chocolate,
- Rgba32.Coral,
- Rgba32.CornflowerBlue,
- Rgba32.Cornsilk,
- Rgba32.Crimson,
- Rgba32.Cyan,
- Rgba32.DarkBlue,
- Rgba32.DarkCyan,
- Rgba32.DarkGoldenrod,
- Rgba32.DarkGray,
- Rgba32.DarkGreen,
- Rgba32.DarkKhaki,
- Rgba32.DarkMagenta,
- Rgba32.DarkOliveGreen,
- Rgba32.DarkOrange,
- Rgba32.DarkOrchid,
- Rgba32.DarkRed,
- Rgba32.DarkSalmon,
- Rgba32.DarkSeaGreen,
- Rgba32.DarkSlateBlue,
- Rgba32.DarkSlateGray,
- Rgba32.DarkTurquoise,
- Rgba32.DarkViolet,
- Rgba32.DeepPink,
- Rgba32.DeepSkyBlue,
- Rgba32.DimGray,
- Rgba32.DodgerBlue,
- Rgba32.Firebrick,
- Rgba32.FloralWhite,
- Rgba32.ForestGreen,
- Rgba32.Fuchsia,
- Rgba32.Gainsboro,
- Rgba32.GhostWhite,
- Rgba32.Gold,
- Rgba32.Goldenrod,
- Rgba32.Gray,
- Rgba32.Green,
- Rgba32.GreenYellow,
- Rgba32.Honeydew,
- Rgba32.HotPink,
- Rgba32.IndianRed,
- Rgba32.Indigo,
- Rgba32.Ivory,
- Rgba32.Khaki,
- Rgba32.Lavender,
- Rgba32.LavenderBlush,
- Rgba32.LawnGreen,
- Rgba32.LemonChiffon,
- Rgba32.LightBlue,
- Rgba32.LightCoral,
- Rgba32.LightCyan,
- Rgba32.LightGoldenrodYellow,
- Rgba32.LightGray,
- Rgba32.LightGreen,
- Rgba32.LightPink,
- Rgba32.LightSalmon,
- Rgba32.LightSeaGreen,
- Rgba32.LightSkyBlue,
- Rgba32.LightSlateGray,
- Rgba32.LightSteelBlue,
- Rgba32.LightYellow,
- Rgba32.Lime,
- Rgba32.LimeGreen,
- Rgba32.Linen,
- Rgba32.Magenta,
- Rgba32.Maroon,
- Rgba32.MediumAquamarine,
- Rgba32.MediumBlue,
- Rgba32.MediumOrchid,
- Rgba32.MediumPurple,
- Rgba32.MediumSeaGreen,
- Rgba32.MediumSlateBlue,
- Rgba32.MediumSpringGreen,
- Rgba32.MediumTurquoise,
- Rgba32.MediumVioletRed,
- Rgba32.MidnightBlue,
- Rgba32.MintCream,
- Rgba32.MistyRose,
- Rgba32.Moccasin,
- Rgba32.NavajoWhite,
- Rgba32.Navy,
- Rgba32.OldLace,
- Rgba32.Olive,
- Rgba32.OliveDrab,
- Rgba32.Orange,
- Rgba32.OrangeRed,
- Rgba32.Orchid,
- Rgba32.PaleGoldenrod,
- Rgba32.PaleGreen,
- Rgba32.PaleTurquoise,
- Rgba32.PaleVioletRed,
- Rgba32.PapayaWhip,
- Rgba32.PeachPuff,
- Rgba32.Peru,
- Rgba32.Pink,
- Rgba32.Plum,
- Rgba32.PowderBlue,
- Rgba32.Purple,
- Rgba32.RebeccaPurple,
- Rgba32.Red,
- Rgba32.RosyBrown,
- Rgba32.RoyalBlue,
- Rgba32.SaddleBrown,
- Rgba32.Salmon,
- Rgba32.SandyBrown,
- Rgba32.SeaGreen,
- Rgba32.SeaShell,
- Rgba32.Sienna,
- Rgba32.Silver,
- Rgba32.SkyBlue,
- Rgba32.SlateBlue,
- Rgba32.SlateGray,
- Rgba32.Snow,
- Rgba32.SpringGreen,
- Rgba32.SteelBlue,
- Rgba32.Tan,
- Rgba32.Teal,
- Rgba32.Thistle,
- Rgba32.Tomato,
- Rgba32.Transparent,
- Rgba32.Turquoise,
- Rgba32.Violet,
- Rgba32.Wheat,
- Rgba32.White,
- Rgba32.WhiteSmoke,
- Rgba32.Yellow,
- Rgba32.YellowGreen
- };
+ public static readonly Rgba32[] WernerColors =
+ {
+ Rgba32.FromHex("#f1e9cd"),
+ Rgba32.FromHex("#f2e7cf"),
+ Rgba32.FromHex("#ece6d0"),
+ Rgba32.FromHex("#f2eacc"),
+ Rgba32.FromHex("#f3e9ca"),
+ Rgba32.FromHex("#f2ebcd"),
+ Rgba32.FromHex("#e6e1c9"),
+ Rgba32.FromHex("#e2ddc6"),
+ Rgba32.FromHex("#cbc8b7"),
+ Rgba32.FromHex("#bfbbb0"),
+ Rgba32.FromHex("#bebeb3"),
+ Rgba32.FromHex("#b7b5ac"),
+ Rgba32.FromHex("#bab191"),
+ Rgba32.FromHex("#9c9d9a"),
+ Rgba32.FromHex("#8a8d84"),
+ Rgba32.FromHex("#5b5c61"),
+ Rgba32.FromHex("#555152"),
+ Rgba32.FromHex("#413f44"),
+ Rgba32.FromHex("#454445"),
+ Rgba32.FromHex("#423937"),
+ Rgba32.FromHex("#433635"),
+ Rgba32.FromHex("#252024"),
+ Rgba32.FromHex("#241f20"),
+ Rgba32.FromHex("#281f3f"),
+ Rgba32.FromHex("#1c1949"),
+ Rgba32.FromHex("#4f638d"),
+ Rgba32.FromHex("#383867"),
+ Rgba32.FromHex("#5c6b8f"),
+ Rgba32.FromHex("#657abb"),
+ Rgba32.FromHex("#6f88af"),
+ Rgba32.FromHex("#7994b5"),
+ Rgba32.FromHex("#6fb5a8"),
+ Rgba32.FromHex("#719ba2"),
+ Rgba32.FromHex("#8aa1a6"),
+ Rgba32.FromHex("#d0d5d3"),
+ Rgba32.FromHex("#8590ae"),
+ Rgba32.FromHex("#3a2f52"),
+ Rgba32.FromHex("#39334a"),
+ Rgba32.FromHex("#6c6d94"),
+ Rgba32.FromHex("#584c77"),
+ Rgba32.FromHex("#533552"),
+ Rgba32.FromHex("#463759"),
+ Rgba32.FromHex("#bfbac0"),
+ Rgba32.FromHex("#77747f"),
+ Rgba32.FromHex("#4a475c"),
+ Rgba32.FromHex("#b8bfaf"),
+ Rgba32.FromHex("#b2b599"),
+ Rgba32.FromHex("#979c84"),
+ Rgba32.FromHex("#5d6161"),
+ Rgba32.FromHex("#61ac86"),
+ Rgba32.FromHex("#a4b6a7"),
+ Rgba32.FromHex("#adba98"),
+ Rgba32.FromHex("#93b778"),
+ Rgba32.FromHex("#7d8c55"),
+ Rgba32.FromHex("#33431e"),
+ Rgba32.FromHex("#7c8635"),
+ Rgba32.FromHex("#8e9849"),
+ Rgba32.FromHex("#c2c190"),
+ Rgba32.FromHex("#67765b"),
+ Rgba32.FromHex("#ab924b"),
+ Rgba32.FromHex("#c8c76f"),
+ Rgba32.FromHex("#ccc050"),
+ Rgba32.FromHex("#ebdd99"),
+ Rgba32.FromHex("#ab9649"),
+ Rgba32.FromHex("#dbc364"),
+ Rgba32.FromHex("#e6d058"),
+ Rgba32.FromHex("#ead665"),
+ Rgba32.FromHex("#d09b2c"),
+ Rgba32.FromHex("#a36629"),
+ Rgba32.FromHex("#a77d35"),
+ Rgba32.FromHex("#f0d696"),
+ Rgba32.FromHex("#d7c485"),
+ Rgba32.FromHex("#f1d28c"),
+ Rgba32.FromHex("#efcc83"),
+ Rgba32.FromHex("#f3daa7"),
+ Rgba32.FromHex("#dfa837"),
+ Rgba32.FromHex("#ebbc71"),
+ Rgba32.FromHex("#d17c3f"),
+ Rgba32.FromHex("#92462f"),
+ Rgba32.FromHex("#be7249"),
+ Rgba32.FromHex("#bb603c"),
+ Rgba32.FromHex("#c76b4a"),
+ Rgba32.FromHex("#a75536"),
+ Rgba32.FromHex("#b63e36"),
+ Rgba32.FromHex("#b5493a"),
+ Rgba32.FromHex("#cd6d57"),
+ Rgba32.FromHex("#711518"),
+ Rgba32.FromHex("#e9c49d"),
+ Rgba32.FromHex("#eedac3"),
+ Rgba32.FromHex("#eecfbf"),
+ Rgba32.FromHex("#ce536b"),
+ Rgba32.FromHex("#b74a70"),
+ Rgba32.FromHex("#b7757c"),
+ Rgba32.FromHex("#612741"),
+ Rgba32.FromHex("#7a4848"),
+ Rgba32.FromHex("#3f3033"),
+ Rgba32.FromHex("#8d746f"),
+ Rgba32.FromHex("#4d3635"),
+ Rgba32.FromHex("#6e3b31"),
+ Rgba32.FromHex("#864735"),
+ Rgba32.FromHex("#553d3a"),
+ Rgba32.FromHex("#613936"),
+ Rgba32.FromHex("#7a4b3a"),
+ Rgba32.FromHex("#946943"),
+ Rgba32.FromHex("#c39e6d"),
+ Rgba32.FromHex("#513e32"),
+ Rgba32.FromHex("#8b7859"),
+ Rgba32.FromHex("#9b856b"),
+ Rgba32.FromHex("#766051"),
+ Rgba32.FromHex("#453b32")
+ };
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs
index 08530c2bb5..7e093de042 100644
--- a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs
+++ b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs
@@ -14,9 +14,10 @@ namespace SixLabors.ImageSharp.PixelFormats
where TPixel : struct, IPixel
{
///
- /// Thread-safe backing field for .
+ /// Thread-safe backing field for the constant palettes.
///
private static readonly Lazy WebSafePaletteLazy = new Lazy(GetWebSafePalette, true);
+ private static readonly Lazy WernerPaletteLazy = new Lazy(GetWernerPalette, true);
///
/// Represents a matching the W3C definition that has an hex value of #F0F8FF.
@@ -729,22 +730,32 @@ namespace SixLabors.ImageSharp.PixelFormats
public static readonly TPixel YellowGreen = ColorBuilder.FromRGBA(154, 205, 50, 255);
///
- /// Gets a matching the W3C definition of web safe colors.
+ /// Gets a collection of web safe, colors as defined in the CSS Color Module Level 4.
///
public static TPixel[] WebSafePalette => WebSafePaletteLazy.Value;
- private static TPixel[] GetWebSafePalette()
+ ///
+ /// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
+ /// The hex codes were collected and defined by Nicholas Rougeux
+ ///
+ public static TPixel[] WernerPalette => WernerPaletteLazy.Value;
+
+ private static TPixel[] GetWebSafePalette() => GetPalette(ColorConstants.WebSafeColors);
+
+ private static TPixel[] GetWernerPalette() => GetPalette(ColorConstants.WernerColors);
+
+ private static TPixel[] GetPalette(Rgba32[] palette)
{
- Rgba32[] constants = ColorConstants.WebSafeColors;
- var safe = new TPixel[constants.Length + 1];
+ var converted = new TPixel[palette.Length];
- Span constantsBytes = MemoryMarshal.Cast(constants.AsSpan());
+ Span constantsBytes = MemoryMarshal.Cast(palette.AsSpan());
PixelOperations.Instance.FromRgba32Bytes(
Configuration.Default,
constantsBytes,
- safe,
- constants.Length);
- return safe;
+ converted,
+ palette.Length);
+
+ return converted;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/KnownQuantizers.cs b/src/ImageSharp/Processing/KnownQuantizers.cs
index fe98063104..e4a7a75d5f 100644
--- a/src/ImageSharp/Processing/KnownQuantizers.cs
+++ b/src/ImageSharp/Processing/KnownQuantizers.cs
@@ -12,20 +12,23 @@ namespace SixLabors.ImageSharp.Processing
{
///
/// Gets the adaptive Octree quantizer. Fast with good quality.
- /// The quantizer only supports a single alpha value.
///
public static IQuantizer Octree { get; } = new OctreeQuantizer();
///
/// Gets the Xiaolin Wu's Color Quantizer which generates high quality output.
- /// The quantizer supports multiple alpha values.
///
public static IQuantizer Wu { get; } = new WuQuantizer();
///
- /// Gets the palette based, Using the collection of web-safe colors.
- /// The quantizer supports multiple alpha values.
+ /// Gets the palette based quantizer consisting of web safe colors as defined in the CSS Color Module Level 4.
///
- public static IQuantizer Palette { get; } = new PaletteQuantizer();
+ public static IQuantizer WebSafe { get; } = new WebSafePaletteQuantizer();
+
+ ///
+ /// Gets the palette based quantizer consisting of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
+ /// The hex codes were collected and defined by Nicholas Rougeux
+ ///
+ public static IQuantizer Werner { get; } = new WernerPaletteQuantizer();
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs
index 38962c7680..d49023886b 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs
@@ -15,11 +15,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
///
public class OctreeQuantizer : IQuantizer
{
- ///
- /// The default maximum number of colors to use when quantizing the image.
- ///
- public const int DefaultMaxColors = 256;
-
///
/// Initializes a new instance of the class.
///
@@ -42,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
///
/// Whether to apply dithering to the output image
public OctreeQuantizer(bool dither)
- : this(GetDiffuser(dither), DefaultMaxColors)
+ : this(GetDiffuser(dither), QuantizerConstants.MaxColors)
{
}
@@ -51,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
///
/// The error diffusion algorithm, if any, to apply to the output image
public OctreeQuantizer(IErrorDiffuser diffuser)
- : this(diffuser, DefaultMaxColors)
+ : this(diffuser, QuantizerConstants.MaxColors)
{
}
@@ -63,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public OctreeQuantizer(IErrorDiffuser diffuser, int maxColors)
{
this.Diffuser = diffuser;
- this.MaxColors = maxColors.Clamp(1, DefaultMaxColors);
+ this.MaxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
}
///
@@ -84,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors)
where TPixel : struct, IPixel
{
- maxColors = maxColors.Clamp(1, DefaultMaxColors);
+ maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
return new OctreeFrameQuantizer(this, maxColors);
}
diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs
index cab3af6de9..f8a19f8c40 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
@@ -23,27 +22,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
///
private readonly TPixel[] palette;
- ///
- /// The vector representation of the image palette.
- ///
- private readonly Vector4[] paletteVector;
-
///
/// Initializes a new instance of the class.
///
- /// The to configure internal operations.
/// The palette quantizer.
/// An array of all colors in the palette.
- public PaletteFrameQuantizer(Configuration configuration, PaletteQuantizer quantizer, TPixel[] colors)
- : base(quantizer, true)
- {
- // TODO: Why is this value constrained? Gif has limitations but theoretically
- // we might want to reduce the palette of an image to greater than that limitation.
- Guard.MustBeBetweenOrEqualTo(colors.Length, 1, 256, nameof(colors));
- this.palette = colors;
- this.paletteVector = new Vector4[this.palette.Length];
- PixelOperations.Instance.ToScaledVector4(configuration, this.palette, this.paletteVector);
- }
+ public PaletteFrameQuantizer(IQuantizer quantizer, TPixel[] colors)
+ : base(quantizer, true) => this.palette = colors;
///
protected override void SecondPass(
diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs
index 361791253e..6b2be3d038 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs
@@ -8,18 +8,18 @@ using SixLabors.ImageSharp.Processing.Processors.Dithering;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
///
- /// Allows the quantization of images pixels using web safe colors defined in the CSS Color Module Level 4.
- /// Override this class to provide your own palette.
+ /// Allows the quantization of images pixels using color palettes.
+ /// Override this class to provide your own palette.
///
- /// By default the quantizer uses dithering and the
+ /// By default the quantizer uses dithering.
///
///
- public class PaletteQuantizer : IQuantizer
+ public abstract class PaletteQuantizer : IQuantizer
{
///
/// Initializes a new instance of the class.
///
- public PaletteQuantizer()
+ protected PaletteQuantizer()
: this(true)
{
}
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Initializes a new instance of the class.
///
/// Whether to apply dithering to the output image
- public PaletteQuantizer(bool dither)
+ protected PaletteQuantizer(bool dither)
: this(GetDiffuser(dither))
{
}
@@ -37,42 +37,40 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Initializes a new instance of the class.
///
/// The error diffusion algorithm, if any, to apply to the output image
- public PaletteQuantizer(IErrorDiffuser diffuser) => this.Diffuser = diffuser;
+ protected PaletteQuantizer(IErrorDiffuser diffuser) => this.Diffuser = diffuser;
///
public IErrorDiffuser Diffuser { get; }
///
- public virtual IFrameQuantizer CreateFrameQuantizer(Configuration configuration)
- where TPixel : struct, IPixel
- => this.CreateFrameQuantizer(configuration, () => NamedColors.WebSafePalette);
+ public abstract IFrameQuantizer CreateFrameQuantizer(Configuration configuration)
+ where TPixel : struct, IPixel;
///
- public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors)
+ public abstract IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors)
+ where TPixel : struct, IPixel;
+
+ ///
+ /// Creates the generic frame quantizer.
+ ///
+ /// The pixel format.
+ /// The to configure internal operations.
+ /// The color palette.
+ /// The maximum number of colors to hold in the color palette.
+ /// The
+ protected IFrameQuantizer CreateFrameQuantizer(Configuration configuration, TPixel[] palette, int maxColors)
where TPixel : struct, IPixel
{
- TPixel[] websafe = NamedColors.WebSafePalette;
- int max = Math.Min(maxColors, websafe.Length);
+ int max = Math.Min(QuantizerConstants.MaxColors, Math.Min(maxColors, palette.Length));
- if (max != websafe.Length)
+ if (max != palette.Length)
{
- return this.CreateFrameQuantizer(configuration, () => NamedColors.WebSafePalette.AsSpan(0, max).ToArray());
+ return new PaletteFrameQuantizer(this, palette.AsSpan(0, max).ToArray());
}
- return this.CreateFrameQuantizer(configuration, () => websafe);
+ return new PaletteFrameQuantizer(this, palette);
}
- ///
- /// Gets the palette to use to quantize the image.
- ///
- /// The pixel format.
- /// The to configure internal operations
- /// The method to return the palette.
- /// The
- public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, Func paletteFunction)
- where TPixel : struct, IPixel
- => new PaletteFrameQuantizer(configuration, this, paletteFunction.Invoke());
-
private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null;
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs
new file mode 100644
index 0000000000..a350adfc0c
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs
@@ -0,0 +1,110 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Processors.Dithering;
+
+namespace SixLabors.ImageSharp.Processing.Processors.Quantization
+{
+ ///
+ /// A generic palette quantizer.
+ ///
+ /// The pixel format.
+ public class PaletteQuantizer : IQuantizer
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The color palette to use.
+ public PaletteQuantizer(TPixel[] palette)
+ : this(palette, true)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The color palette to use.
+ /// Whether to apply dithering to the output image
+ public PaletteQuantizer(TPixel[] palette, bool dither)
+ : this(palette, GetDiffuser(dither))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The color palette to use.
+ /// The error diffusion algorithm, if any, to apply to the output image
+ public PaletteQuantizer(TPixel[] palette, IErrorDiffuser diffuser)
+ {
+ Guard.MustBeBetweenOrEqualTo(palette.Length, QuantizerConstants.MinColors, QuantizerConstants.MaxColors, nameof(palette));
+ this.Palette = palette;
+ this.Diffuser = diffuser;
+ }
+
+ ///
+ public IErrorDiffuser Diffuser { get; }
+
+ ///
+ /// Gets the palette.
+ ///
+ public TPixel[] Palette { get; }
+
+ ///
+ /// Creates the generic frame quantizer.
+ ///
+ /// The to configure internal operations.
+ /// The .
+ public IFrameQuantizer CreateFrameQuantizer(Configuration configuration)
+ => ((IQuantizer)this).CreateFrameQuantizer(configuration);
+
+ ///
+ /// Creates the generic frame quantizer.
+ ///
+ /// The to configure internal operations.
+ /// The maximum number of colors to hold in the color palette.
+ /// The .
+ public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors)
+ => ((IQuantizer)this).CreateFrameQuantizer(configuration, maxColors);
+
+ ///
+ IFrameQuantizer IQuantizer.CreateFrameQuantizer(Configuration configuration)
+ {
+ if (!typeof(TPixel).Equals(typeof(TPixel1)))
+ {
+ throw new InvalidOperationException("Generic method type must be the same as class type.");
+ }
+
+ TPixel[] paletteRef = this.Palette;
+ return new PaletteFrameQuantizer(this, Unsafe.As(ref paletteRef));
+ }
+
+ ///
+ IFrameQuantizer IQuantizer.CreateFrameQuantizer(Configuration configuration, int maxColors)
+ {
+ if (!typeof(TPixel).Equals(typeof(TPixel1)))
+ {
+ throw new InvalidOperationException("Generic method type must be the same as class type.");
+ }
+
+ TPixel[] paletteRef = this.Palette;
+ TPixel1[] castPalette = Unsafe.As(ref paletteRef);
+
+ maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
+ int max = Math.Min(maxColors, castPalette.Length);
+
+ if (max != castPalette.Length)
+ {
+ return new PaletteFrameQuantizer(this, castPalette.AsSpan(0, max).ToArray());
+ }
+
+ return new PaletteFrameQuantizer(this, castPalette);
+ }
+
+ private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null;
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs
new file mode 100644
index 0000000000..d79a91c301
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Processing.Processors.Quantization
+{
+ ///
+ /// Contains color quantization specific constants.
+ ///
+ internal static class QuantizerConstants
+ {
+ ///
+ /// The minimum number of colors to use when quantizing an image.
+ ///
+ public const int MinColors = 1;
+
+ ///
+ /// The maximum number of colors to use when quantizing an image.
+ ///
+ public const int MaxColors = 256;
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs
new file mode 100644
index 0000000000..93630a9166
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Processors.Dithering;
+
+namespace SixLabors.ImageSharp.Processing.Processors.Quantization
+{
+ ///
+ /// A palette quantizer consisting of web safe colors as defined in the CSS Color Module Level 4.
+ ///
+ public class WebSafePaletteQuantizer : PaletteQuantizer
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public WebSafePaletteQuantizer()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Whether to apply dithering to the output image
+ public WebSafePaletteQuantizer(bool dither)
+ : base(dither)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The error diffusion algorithm, if any, to apply to the output image
+ public WebSafePaletteQuantizer(IErrorDiffuser diffuser)
+ : base(diffuser)
+ {
+ }
+
+ ///
+ public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration)
+ => this.CreateFrameQuantizer(configuration, NamedColors.WebSafePalette.Length);
+
+ ///
+ public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors)
+ => this.CreateFrameQuantizer(configuration, NamedColors.WebSafePalette, maxColors);
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs
new file mode 100644
index 0000000000..2ff9f5090c
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Processors.Dithering;
+
+namespace SixLabors.ImageSharp.Processing.Processors.Quantization
+{
+ ///
+ /// A palette quantizer consisting of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
+ /// The hex codes were collected and defined by Nicholas Rougeux
+ ///
+ public class WernerPaletteQuantizer : PaletteQuantizer
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public WernerPaletteQuantizer()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Whether to apply dithering to the output image
+ public WernerPaletteQuantizer(bool dither)
+ : base(dither)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The error diffusion algorithm, if any, to apply to the output image
+ public WernerPaletteQuantizer(IErrorDiffuser diffuser)
+ : base(diffuser)
+ {
+ }
+
+ ///
+ public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration)
+ => this.CreateFrameQuantizer(configuration, NamedColors.WernerPalette.Length);
+
+ ///
+ public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors)
+ => this.CreateFrameQuantizer(configuration, NamedColors.WernerPalette, maxColors);
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs
index 4cd09a14f4..eb8b0fec91 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs
@@ -14,11 +14,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
///
public class WuQuantizer : IQuantizer
{
- ///
- /// The default maximum number of colors to use when quantizing the image.
- ///
- public const int DefaultMaxColors = 256;
-
///
/// Initializes a new instance of the class.
///
@@ -41,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
///
/// Whether to apply dithering to the output image
public WuQuantizer(bool dither)
- : this(GetDiffuser(dither), DefaultMaxColors)
+ : this(GetDiffuser(dither), QuantizerConstants.MaxColors)
{
}
@@ -50,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
///
/// The error diffusion algorithm, if any, to apply to the output image
public WuQuantizer(IErrorDiffuser diffuser)
- : this(diffuser, DefaultMaxColors)
+ : this(diffuser, QuantizerConstants.MaxColors)
{
}
@@ -62,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public WuQuantizer(IErrorDiffuser diffuser, int maxColors)
{
this.Diffuser = diffuser;
- this.MaxColors = maxColors.Clamp(1, DefaultMaxColors);
+ this.MaxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
}
///
@@ -83,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors)
where TPixel : struct, IPixel
{
- maxColors = maxColors.Clamp(1, DefaultMaxColors);
+ maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
return new WuFrameQuantizer(this, maxColors);
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs
index 12e74ccdbb..89eb63d629 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs
@@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public void GifCore()
{
// Try to get as close to System.Drawing's output as possible
- var options = new GifEncoder { Quantizer = new PaletteQuantizer(false) };
+ var options = new GifEncoder { Quantizer = new WebSafePaletteQuantizer(false) };
using (var memoryStream = new MemoryStream())
{
this.bmpCore.SaveAsGif(memoryStream, options);
diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs
index 9b94347f34..bf9627f4c1 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs
@@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
this.ForEachImageSharpImage((img, ms) =>
{
// Try to get as close to System.Drawing's output as possible
- var options = new GifEncoder { Quantizer = new PaletteQuantizer(false) };
+ var options = new GifEncoder { Quantizer = new WebSafePaletteQuantizer(false) };
img.Save(ms, options); return null;
});
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs
index 962b34eb7c..639d1594ee 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs
@@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
{
using (var memoryStream = new MemoryStream())
{
- var options = new PngEncoder { Quantizer = KnownQuantizers.Palette };
+ var options = new PngEncoder { Quantizer = KnownQuantizers.WebSafe };
this.bmpCore.SaveAsPng(memoryStream, options);
}
}
@@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
{
using (var memoryStream = new MemoryStream())
{
- var options = new PngEncoder { Quantizer = new PaletteQuantizer(false) };
+ var options = new PngEncoder { Quantizer = new WebSafePaletteQuantizer(false) };
this.bmpCore.SaveAsPng(memoryStream, options);
}
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs
new file mode 100644
index 0000000000..bf9b1af338
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs
@@ -0,0 +1,133 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+using BenchmarkDotNet.Attributes;
+
+using SixLabors.ImageSharp.Formats.Jpeg.Components;
+using SixLabors.ImageSharp.Memory;
+// ReSharper disable InconsistentNaming
+
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
+{
+ public class Block8x8F_CopyTo1x1
+ {
+ private Block8x8F block;
+
+ private Buffer2D buffer;
+
+ private BufferArea destArea;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ if (!SimdUtils.IsAvx2CompatibleArchitecture)
+ {
+ throw new InvalidOperationException("Benchmark Block8x8F_CopyTo1x1 is invalid on platforms without AVX2 support.");
+ }
+
+ this.buffer = Configuration.Default.MemoryAllocator.Allocate2D(1000, 500);
+ this.destArea = this.buffer.GetArea(200, 100, 64, 64);
+ }
+
+ [Benchmark(Baseline = true)]
+ public void Original()
+ {
+ ref byte selfBase = ref Unsafe.As(ref this.block);
+ ref byte destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin());
+ int destStride = this.destArea.Stride * sizeof(float);
+
+ CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
+ CopyRowImpl(ref selfBase, ref destBase, destStride, 1);
+ CopyRowImpl(ref selfBase, ref destBase, destStride, 2);
+ CopyRowImpl(ref selfBase, ref destBase, destStride, 3);
+ CopyRowImpl(ref selfBase, ref destBase, destStride, 4);
+ CopyRowImpl(ref selfBase, ref destBase, destStride, 5);
+ CopyRowImpl(ref selfBase, ref destBase, destStride, 6);
+ CopyRowImpl(ref selfBase, ref destBase, destStride, 7);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row)
+ {
+ ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float));
+ ref byte d = ref Unsafe.Add(ref destBase, row * destStride);
+ Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float));
+ }
+
+ [Benchmark]
+ public void UseVector8()
+ {
+ ref Block8x8F s = ref this.block;
+ ref float origin = ref this.destArea.GetReferenceToOrigin();
+ int stride = this.destArea.Stride;
+
+ ref Vector d0 = ref Unsafe.As>(ref origin);
+ ref Vector d1 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride));
+ ref Vector d2 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 2));
+ ref Vector d3 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 3));
+ ref Vector d4 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 4));
+ ref Vector d5 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 5));
+ ref Vector d6 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 6));
+ ref Vector d7 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 7));
+
+ Vector row0 = Unsafe.As>(ref s.V0L);
+ Vector row1 = Unsafe.As>(ref s.V1L);
+ Vector row2 = Unsafe.As>(ref s.V2L);
+ Vector row3 = Unsafe.As>(ref s.V3L);
+ Vector row4 = Unsafe.As>(ref s.V4L);
+ Vector row5 = Unsafe.As>(ref s.V5L);
+ Vector row6 = Unsafe.As>(ref s.V6L);
+ Vector row7 = Unsafe.As>(ref s.V7L);
+
+ d0 = row0;
+ d1 = row1;
+ d2 = row2;
+ d3 = row3;
+ d4 = row4;
+ d5 = row5;
+ d6 = row6;
+ d7 = row7;
+ }
+
+ [Benchmark]
+ public void UseVector8_V2()
+ {
+ ref Block8x8F s = ref this.block;
+ ref float origin = ref this.destArea.GetReferenceToOrigin();
+ int stride = this.destArea.Stride;
+
+ ref Vector d0 = ref Unsafe.As>(ref origin);
+ ref Vector d1 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride));
+ ref Vector d2 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 2));
+ ref Vector d3 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 3));
+ ref Vector d4 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 4));
+ ref Vector d5 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 5));
+ ref Vector d6 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 6));
+ ref Vector d7 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 7));
+
+ d0 = Unsafe.As>(ref s.V0L);
+ d1 = Unsafe.As>(ref s.V1L);
+ d2 = Unsafe.As>(ref s.V2L);
+ d3 = Unsafe.As>(ref s.V3L);
+ d4 = Unsafe.As>(ref s.V4L);
+ d5 = Unsafe.As>(ref s.V5L);
+ d6 = Unsafe.As>(ref s.V6L);
+ d7 = Unsafe.As>(ref s.V7L);
+ }
+
+ // RESULTS:
+ //
+ // Method | Mean | Error | StdDev | Scaled |
+ // -------------- |---------:|----------:|----------:|-------:|
+ // Original | 22.53 ns | 0.1660 ns | 0.1553 ns | 1.00 |
+ // UseVector8 | 21.59 ns | 0.3079 ns | 0.2571 ns | 0.96 |
+ // UseVector8_V2 | 22.57 ns | 0.1699 ns | 0.1506 ns | 1.00 |
+ //
+ // Conclusion:
+ // Doesn't worth to bother with this
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs
new file mode 100644
index 0000000000..65176af5bb
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs
@@ -0,0 +1,404 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+using BenchmarkDotNet.Attributes;
+
+using SixLabors.ImageSharp.Formats.Jpeg.Components;
+using SixLabors.ImageSharp.Memory;
+// ReSharper disable InconsistentNaming
+
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
+{
+ public class Block8x8F_CopyTo2x2
+ {
+ private Block8x8F block;
+
+ private Buffer2D buffer;
+
+ private BufferArea destArea;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ this.buffer = Configuration.Default.MemoryAllocator.Allocate2D(1000, 500);
+ this.destArea = this.buffer.GetArea(200, 100, 128, 128);
+ }
+
+ [Benchmark(Baseline = true)]
+ public void Original()
+ {
+ ref float destBase = ref this.destArea.GetReferenceToOrigin();
+ int destStride = this.destArea.Stride;
+
+ ref Block8x8F src = ref this.block;
+
+ WidenCopyImpl2x2(ref src, ref destBase, 0, destStride);
+ WidenCopyImpl2x2(ref src, ref destBase, 1, destStride);
+ WidenCopyImpl2x2(ref src, ref destBase, 2, destStride);
+ WidenCopyImpl2x2(ref src, ref destBase, 3, destStride);
+ WidenCopyImpl2x2(ref src, ref destBase, 4, destStride);
+ WidenCopyImpl2x2(ref src, ref destBase, 5, destStride);
+ WidenCopyImpl2x2(ref src, ref destBase, 6, destStride);
+ WidenCopyImpl2x2(ref src, ref destBase, 7, destStride);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void WidenCopyImpl2x2(ref Block8x8F src, ref float destBase, int row, int destStride)
+ {
+ ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
+ ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1);
+ ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride);
+
+ Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X;
+ Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X;
+ Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y;
+ Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y;
+ Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z;
+ Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z;
+ Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W;
+ Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W;
+
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W;
+
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W;
+
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W;
+ Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W;
+ }
+
+ [Benchmark]
+ public void Original_V2()
+ {
+ ref float destBase = ref this.destArea.GetReferenceToOrigin();
+ int destStride = this.destArea.Stride;
+
+ ref Block8x8F src = ref this.block;
+
+ WidenCopyImpl2x2_V2(ref src, ref destBase, 0, destStride);
+ WidenCopyImpl2x2_V2(ref src, ref destBase, 1, destStride);
+ WidenCopyImpl2x2_V2(ref src, ref destBase, 2, destStride);
+ WidenCopyImpl2x2_V2(ref src, ref destBase, 3, destStride);
+ WidenCopyImpl2x2_V2(ref src, ref destBase, 4, destStride);
+ WidenCopyImpl2x2_V2(ref src, ref destBase, 5, destStride);
+ WidenCopyImpl2x2_V2(ref src, ref destBase, 6, destStride);
+ WidenCopyImpl2x2_V2(ref src, ref destBase, 7, destStride);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void WidenCopyImpl2x2_V2(ref Block8x8F src, ref float destBase, int row, int destStride)
+ {
+ ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
+ ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1);
+ ref float dest0 = ref Unsafe.Add(ref destBase, row * 2 * destStride);
+
+ Unsafe.Add(ref dest0, 0) = selfLeft.X;
+ Unsafe.Add(ref dest0, 1) = selfLeft.X;
+ Unsafe.Add(ref dest0, 2) = selfLeft.Y;
+ Unsafe.Add(ref dest0, 3) = selfLeft.Y;
+ Unsafe.Add(ref dest0, 4) = selfLeft.Z;
+ Unsafe.Add(ref dest0, 5) = selfLeft.Z;
+ Unsafe.Add(ref dest0, 6) = selfLeft.W;
+ Unsafe.Add(ref dest0, 7) = selfLeft.W;
+
+ ref float dest1 = ref Unsafe.Add(ref dest0, 8);
+
+ Unsafe.Add(ref dest1, 0) = selfRight.X;
+ Unsafe.Add(ref dest1, 1) = selfRight.X;
+ Unsafe.Add(ref dest1, 2) = selfRight.Y;
+ Unsafe.Add(ref dest1, 3) = selfRight.Y;
+ Unsafe.Add(ref dest1, 4) = selfRight.Z;
+ Unsafe.Add(ref dest1, 5) = selfRight.Z;
+ Unsafe.Add(ref dest1, 6) = selfRight.W;
+ Unsafe.Add(ref dest1, 7) = selfRight.W;
+
+ ref float dest2 = ref Unsafe.Add(ref dest0, destStride);
+
+ Unsafe.Add(ref dest2, 0) = selfLeft.X;
+ Unsafe.Add(ref dest2, 1) = selfLeft.X;
+ Unsafe.Add(ref dest2, 2) = selfLeft.Y;
+ Unsafe.Add(ref dest2, 3) = selfLeft.Y;
+ Unsafe.Add(ref dest2, 4) = selfLeft.Z;
+ Unsafe.Add(ref dest2, 5) = selfLeft.Z;
+ Unsafe.Add(ref dest2, 6) = selfLeft.W;
+ Unsafe.Add(ref dest2, 7) = selfLeft.W;
+
+ ref float dest3 = ref Unsafe.Add(ref dest2, 8);
+
+ Unsafe.Add(ref dest3, 0) = selfRight.X;
+ Unsafe.Add(ref dest3, 1) = selfRight.X;
+ Unsafe.Add(ref dest3, 2) = selfRight.Y;
+ Unsafe.Add(ref dest3, 3) = selfRight.Y;
+ Unsafe.Add(ref dest3, 4) = selfRight.Z;
+ Unsafe.Add(ref dest3, 5) = selfRight.Z;
+ Unsafe.Add(ref dest3, 6) = selfRight.W;
+ Unsafe.Add(ref dest3, 7) = selfRight.W;
+ }
+
+ [Benchmark]
+ public void UseVector2()
+ {
+ ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin());
+ int destStride = this.destArea.Stride / 2;
+
+ ref Block8x8F src = ref this.block;
+
+ WidenCopyImpl2x2_Vector2(ref src, ref destBase, 0, destStride);
+ WidenCopyImpl2x2_Vector2(ref src, ref destBase, 1, destStride);
+ WidenCopyImpl2x2_Vector2(ref src, ref destBase, 2, destStride);
+ WidenCopyImpl2x2_Vector2(ref src, ref destBase, 3, destStride);
+ WidenCopyImpl2x2_Vector2(ref src, ref destBase, 4, destStride);
+ WidenCopyImpl2x2_Vector2(ref src, ref destBase, 5, destStride);
+ WidenCopyImpl2x2_Vector2(ref src, ref destBase, 6, destStride);
+ WidenCopyImpl2x2_Vector2(ref src, ref destBase, 7, destStride);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void WidenCopyImpl2x2_Vector2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
+ {
+ ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
+ ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
+
+ ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride);
+ ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4);
+ ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride);
+ ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4);
+
+ var xLeft = new Vector2(sLeft.X);
+ var yLeft = new Vector2(sLeft.Y);
+ var zLeft = new Vector2(sLeft.Z);
+ var wLeft = new Vector2(sLeft.W);
+
+ var xRight = new Vector2(sRight.X);
+ var yRight = new Vector2(sRight.Y);
+ var zRight = new Vector2(sRight.Z);
+ var wRight = new Vector2(sRight.W);
+
+ dTopLeft = xLeft;
+ Unsafe.Add(ref dTopLeft, 1) = yLeft;
+ Unsafe.Add(ref dTopLeft, 2) = zLeft;
+ Unsafe.Add(ref dTopLeft, 3) = wLeft;
+
+ dTopRight = xRight;
+ Unsafe.Add(ref dTopRight, 1) = yRight;
+ Unsafe.Add(ref dTopRight, 2) = zRight;
+ Unsafe.Add(ref dTopRight, 3) = wRight;
+
+ dBottomLeft = xLeft;
+ Unsafe.Add(ref dBottomLeft, 1) = yLeft;
+ Unsafe.Add(ref dBottomLeft, 2) = zLeft;
+ Unsafe.Add(ref dBottomLeft, 3) = wLeft;
+
+ dBottomRight = xRight;
+ Unsafe.Add(ref dBottomRight, 1) = yRight;
+ Unsafe.Add(ref dBottomRight, 2) = zRight;
+ Unsafe.Add(ref dBottomRight, 3) = wRight;
+ }
+
+ [Benchmark]
+ public void UseVector4()
+ {
+ ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin());
+ int destStride = this.destArea.Stride / 2;
+
+ ref Block8x8F src = ref this.block;
+
+ WidenCopyImpl2x2_Vector4(ref src, ref destBase, 0, destStride);
+ WidenCopyImpl2x2_Vector4(ref src, ref destBase, 1, destStride);
+ WidenCopyImpl2x2_Vector4(ref src, ref destBase, 2, destStride);
+ WidenCopyImpl2x2_Vector4(ref src, ref destBase, 3, destStride);
+ WidenCopyImpl2x2_Vector4(ref src, ref destBase, 4, destStride);
+ WidenCopyImpl2x2_Vector4(ref src, ref destBase, 5, destStride);
+ WidenCopyImpl2x2_Vector4(ref src, ref destBase, 6, destStride);
+ WidenCopyImpl2x2_Vector4(ref src, ref destBase, 7, destStride);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void WidenCopyImpl2x2_Vector4(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
+ {
+ ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
+ ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
+
+ ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride);
+ ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4);
+ ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride);
+ ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4);
+
+ var xLeft = new Vector4(sLeft.X);
+ var yLeft = new Vector4(sLeft.Y);
+ var zLeft = new Vector4(sLeft.Z);
+ var wLeft = new Vector4(sLeft.W);
+
+ var xRight = new Vector4(sRight.X);
+ var yRight = new Vector4(sRight.Y);
+ var zRight = new Vector4(sRight.Z);
+ var wRight = new Vector4(sRight.W);
+
+ Unsafe.As(ref dTopLeft) = xLeft;
+ Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
+ Unsafe.As(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
+ Unsafe.As(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
+
+ Unsafe.As(ref dTopRight) = xRight;
+ Unsafe.As(ref Unsafe.Add(ref dTopRight, 1)) = yRight;
+ Unsafe.As(ref Unsafe.Add(ref dTopRight, 2)) = zRight;
+ Unsafe.As(ref Unsafe.Add(ref dTopRight, 3)) = wRight;
+
+ Unsafe.As(ref dBottomLeft) = xLeft;
+ Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
+ Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
+ Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
+
+ Unsafe.As(ref dBottomRight) = xRight;
+ Unsafe.As(ref Unsafe.Add(ref dBottomRight, 1)) = yRight;
+ Unsafe.As(ref Unsafe.Add(ref dBottomRight, 2)) = zRight;
+ Unsafe.As(ref Unsafe.Add(ref dBottomRight, 3)) = wRight;
+ }
+
+ [Benchmark]
+ public void UseVector4_SafeRightCorner()
+ {
+ ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin());
+ int destStride = this.destArea.Stride / 2;
+
+ ref Block8x8F src = ref this.block;
+
+ WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 0, destStride);
+ WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 1, destStride);
+ WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 2, destStride);
+ WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 3, destStride);
+ WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 4, destStride);
+ WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 5, destStride);
+ WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 6, destStride);
+ WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 7, destStride);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void WidenCopyImpl2x2_Vector4_SafeRightCorner(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
+ {
+ ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
+ ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
+
+ ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride);
+ ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride);
+
+ var xLeft = new Vector4(sLeft.X);
+ var yLeft = new Vector4(sLeft.Y);
+ var zLeft = new Vector4(sLeft.Z);
+ var wLeft = new Vector4(sLeft.W);
+
+ var xRight = new Vector4(sRight.X);
+ var yRight = new Vector4(sRight.Y);
+ var zRight = new Vector4(sRight.Z);
+ var wRight = new Vector2(sRight.W);
+
+ Unsafe.As(ref dTopLeft) = xLeft;
+ Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
+ Unsafe.As(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
+ Unsafe.As(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
+
+ Unsafe.As(ref Unsafe.Add(ref dTopLeft, 4)) = xRight;
+ Unsafe.As(ref Unsafe.Add(ref dTopLeft, 5)) = yRight;
+ Unsafe.As(ref Unsafe.Add(ref dTopLeft, 6)) = zRight;
+ Unsafe.Add(ref dTopLeft, 7) = wRight;
+
+ Unsafe.As(ref dBottomLeft) = xLeft;
+ Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
+ Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
+ Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
+
+ Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight;
+ Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight;
+ Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 6)) = zRight;
+ Unsafe.Add(ref dBottomLeft, 7) = wRight;
+ }
+
+
+ [Benchmark]
+ public void UseVector4_V2()
+ {
+ ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin());
+ int destStride = this.destArea.Stride / 2;
+
+ ref Block8x8F src = ref this.block;
+
+ WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 0, destStride);
+ WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 1, destStride);
+ WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 2, destStride);
+ WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 3, destStride);
+ WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 4, destStride);
+ WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 5, destStride);
+ WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 6, destStride);
+ WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 7, destStride);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void WidenCopyImpl2x2_Vector4_V2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
+ {
+ ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
+ ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
+
+ int offset = 2 * row * destStride;
+ ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset));
+ ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride));
+
+ var xyLeft = new Vector4(sLeft.X);
+ xyLeft.Z = sLeft.Y;
+ xyLeft.W = sLeft.Y;
+
+ var zwLeft = new Vector4(sLeft.Z);
+ zwLeft.Z = sLeft.W;
+ zwLeft.W = sLeft.W;
+
+ var xyRight = new Vector4(sRight.X);
+ xyRight.Z = sRight.Y;
+ xyRight.W = sRight.Y;
+
+ var zwRight = new Vector4(sRight.Z);
+ zwRight.Z = sRight.W;
+ zwRight.W = sRight.W;
+
+ dTopLeft = xyLeft;
+ Unsafe.Add(ref dTopLeft, 1) = zwLeft;
+ Unsafe.Add(ref dTopLeft, 2) = xyRight;
+ Unsafe.Add(ref dTopLeft, 3) = zwRight;
+
+ dBottomLeft = xyLeft;
+ Unsafe.Add(ref dBottomLeft, 1) = zwLeft;
+ Unsafe.Add(ref dBottomLeft, 2) = xyRight;
+ Unsafe.Add(ref dBottomLeft, 3) = zwRight;
+ }
+
+ // RESULTS:
+ // Method | Mean | Error | StdDev | Scaled | ScaledSD |
+ // --------------------------- |---------:|----------:|----------:|-------:|---------:|
+ // Original | 92.69 ns | 2.4722 ns | 2.7479 ns | 1.00 | 0.00 |
+ // Original_V2 | 91.72 ns | 1.2089 ns | 1.0095 ns | 0.99 | 0.03 |
+ // UseVector2 | 86.70 ns | 0.5873 ns | 0.5206 ns | 0.94 | 0.03 |
+ // UseVector4 | 55.42 ns | 0.2482 ns | 0.2322 ns | 0.60 | 0.02 |
+ // UseVector4_SafeRightCorner | 58.97 ns | 0.4152 ns | 0.3884 ns | 0.64 | 0.02 |
+ // UseVector4_V2 | 41.88 ns | 0.3531 ns | 0.3303 ns | 0.45 | 0.01 |
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs
similarity index 96%
rename from tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs
rename to tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs
index fcc5f9a592..5502475d43 100644
--- a/tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs
@@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components;
// ReSharper disable InconsistentNaming
-namespace SixLabors.ImageSharp.Benchmarks.General
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
///
/// The goal of this benchmark is to measure the following Jpeg-related scenario:
@@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Benchmarks.General
private static readonly Vector4 MinusOne = new Vector4(-1);
private static readonly Vector4 Half = new Vector4(0.5f);
- private Block8x8F inputDividend = default(Block8x8F);
- private Block8x8F inputDivisior = default(Block8x8F);
+ private Block8x8F inputDividend;
+ private Block8x8F inputDivisior;
[GlobalSetup]
public void Setup()
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs
new file mode 100644
index 0000000000..29ee402a00
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+// ReSharper disable InconsistentNaming
+
+using System;
+using System.Numerics;
+
+using BenchmarkDotNet.Attributes;
+
+using SixLabors.ImageSharp.Formats.Jpeg.Components;
+
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
+{
+ public class Block8x8F_LoadFromInt16
+ {
+ private Block8x8 source;
+
+ private Block8x8F dest = default;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ if (Vector.Count != 8)
+ {
+ throw new NotSupportedException("Vector.Count != 8");
+ }
+
+ for (short i = 0; i < Block8x8F.Size; i++)
+ {
+ this.source[i] = i;
+ }
+ }
+
+ [Benchmark(Baseline = true)]
+ public void Scalar()
+ {
+ this.dest.LoadFromInt16Scalar(ref this.source);
+ }
+
+ [Benchmark]
+ public void ExtendedAvx2()
+ {
+ this.dest.LoadFromInt16ExtendedAvx2(ref this.source);
+ }
+
+ // RESULT:
+ // Method | Mean | Error | StdDev | Scaled |
+ // ------------- |---------:|----------:|----------:|-------:|
+ // Scalar | 34.88 ns | 0.3296 ns | 0.3083 ns | 1.00 |
+ // ExtendedAvx2 | 21.58 ns | 0.2125 ns | 0.1884 ns | 0.62 |
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs
similarity index 90%
rename from tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs
rename to tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs
index 200af64c25..c7b5802c4f 100644
--- a/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs
@@ -1,4 +1,7 @@
-// ReSharper disable InconsistentNaming
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+// ReSharper disable InconsistentNaming
using System;
using System.Numerics;
@@ -8,7 +11,7 @@ using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
-namespace SixLabors.ImageSharp.Benchmarks.General
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
public class Block8x8F_Round
{
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs
deleted file mode 100644
index 9b968e4db3..0000000000
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System.Drawing;
-using System.IO;
-using BenchmarkDotNet.Attributes;
-
-using SixLabors.ImageSharp.Formats.Jpeg;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Tests;
-using CoreSize = SixLabors.Primitives.Size;
-using SDImage = System.Drawing.Image;
-
-namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
-{
- [Config(typeof(Config.ShortClr))]
- public class DecodeJpeg : BenchmarkBase
- {
- private byte[] jpegBytes;
-
- private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
-
- [Params(TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Calliphora)]
- public string TestImage { get; set; }
-
- [GlobalSetup]
- public void ReadImages()
- {
- if (this.jpegBytes == null)
- {
- this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath);
- }
- }
-
- [Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")]
- public Size JpegSystemDrawing()
- {
- using (var memoryStream = new MemoryStream(this.jpegBytes))
- {
- using (var image = SDImage.FromStream(memoryStream))
- {
- return image.Size;
- }
- }
- }
-
- [Benchmark(Description = "Decode Jpeg - ImageSharp")]
- public CoreSize JpegImageSharp()
- {
- using (var memoryStream = new MemoryStream(this.jpegBytes))
- {
- using (var image = Image.Load(memoryStream, new JpegDecoder()))
- {
- return new CoreSize(image.Width, image.Height);
- }
- }
- }
- }
-}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs
deleted file mode 100644
index be0fe76b82..0000000000
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System.Collections.Generic;
-using BenchmarkDotNet.Attributes;
-using SixLabors.ImageSharp.Formats.Jpeg;
-using SixLabors.ImageSharp.PixelFormats;
-using SDImage = System.Drawing.Image;
-
-namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
-{
- [Config(typeof(Config.ShortClr))]
- public class DecodeJpegMultiple : MultiImageBenchmarkBase
- {
- protected override IEnumerable InputImageSubfoldersOrFiles => new[]
- {
- "Jpg/baseline",
- "Jpg/progressive",
- };
-
- protected override IEnumerable SearchPatterns => new[] { "*.jpg" };
-
- [Benchmark(Description = "DecodeJpegMultiple - ImageSharp")]
- public void DecodeJpegImageSharp()
- {
- this.ForEachStream(ms => Image.Load(ms, new JpegDecoder()));
- }
-
- [Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")]
- public void DecodeJpegSystemDrawing()
- {
- this.ForEachStream(SDImage.FromStream);
- }
- }
-}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs
new file mode 100644
index 0000000000..f8a7556ca5
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Collections.Generic;
+
+using BenchmarkDotNet.Attributes;
+
+using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Tests;
+
+using SDImage = System.Drawing.Image;
+// ReSharper disable InconsistentNaming
+
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
+{
+ ///
+ /// An expensive Jpeg benchmark, running on a wide range of input images, showing aggregate results.
+ ///
+ [Config(typeof(MultiImageBenchmarkBase.Config))]
+ public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase
+ {
+ protected override IEnumerable InputImageSubfoldersOrFiles =>
+ new[]
+ {
+ TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome,
+ TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr,
+ TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
+ TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr,
+ TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr,
+ };
+
+ [Params(InputImageCategory.AllImages)]
+ public override InputImageCategory InputCategory { get; set; }
+
+ [Benchmark]
+ public void ImageSharp()
+ {
+ this.ForEachStream(ms => Image.Load(ms, new JpegDecoder()));
+ }
+
+ [Benchmark(Baseline = true)]
+ public void SystemDrawing()
+ {
+ this.ForEachStream(SDImage.FromStream);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
new file mode 100644
index 0000000000..fe112042ef
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
@@ -0,0 +1,119 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Drawing;
+using System.IO;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.Jobs;
+
+using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Tests;
+using CoreSize = SixLabors.Primitives.Size;
+using SDImage = System.Drawing.Image;
+// ReSharper disable InconsistentNaming
+
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
+{
+ ///
+ /// Image-specific Jpeg benchmarks
+ ///
+ [Config(typeof(Config.ShortClr))]
+ public class DecodeJpeg_ImageSpecific
+ {
+ public class Config : ManualConfig
+ {
+ public Config()
+ {
+ // Uncomment if you want to use any of the diagnoser
+ this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser());
+ }
+
+ public class ShortClr : Benchmarks.Config
+ {
+ public ShortClr()
+ {
+ this.Add(
+ //Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3),
+ Job.Core.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3)
+ );
+ }
+ }
+ }
+
+ private byte[] jpegBytes;
+
+ private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
+
+ [Params(
+ TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
+ TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr,
+
+ // The scaled result for the large image "ExifGetString750Transform_Huge420YCbCr"
+ // is almost the same as the result for Jpeg420Exif,
+ // which proves that the execution time for the most common YCbCr 420 path scales linearly.
+ //
+ // TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr,
+
+ TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr
+ )]
+ public string TestImage { get; set; }
+
+
+ [GlobalSetup]
+ public void ReadImages()
+ {
+ if (this.jpegBytes == null)
+ {
+ this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath);
+ }
+ }
+
+ [Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")]
+ public Size JpegSystemDrawing()
+ {
+ using (var memoryStream = new MemoryStream(this.jpegBytes))
+ {
+ using (var image = SDImage.FromStream(memoryStream))
+ {
+ return image.Size;
+ }
+ }
+ }
+
+ [Benchmark(Description = "Decode Jpeg - ImageSharp")]
+ public CoreSize JpegImageSharp()
+ {
+ using (var memoryStream = new MemoryStream(this.jpegBytes))
+ {
+ using (var image = Image.Load(memoryStream, new JpegDecoder(){ IgnoreMetadata = true}))
+ {
+ return new CoreSize(image.Width, image.Height);
+ }
+ }
+ }
+
+ // RESULTS (2018 November 4):
+ //
+ // BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
+ // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
+ // Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
+ // .NET Core SDK=2.1.403
+ // [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
+ //
+ // Method | TestImage | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
+ // ------------------------------- |-------------------------------------------- |-----------:|-----------:|----------:|-------:|---------:|----------:|---------:|---------:|------------:|
+ // 'Decode Jpeg - System.Drawing' | Jpg/baseline/Lake.jpg | 6.117 ms | 0.3923 ms | 0.0222 ms | 1.00 | 0.00 | 62.5000 | - | - | 205.83 KB |
+ // 'Decode Jpeg - ImageSharp' | Jpg/baseline/Lake.jpg | 18.126 ms | 0.6023 ms | 0.0340 ms | 2.96 | 0.01 | - | - | - | 19.97 KB |
+ // | | | | | | | | | | |
+ // 'Decode Jpeg - System.Drawing' | Jpg/baseline/jpeg420exif.jpg | 17.063 ms | 2.6096 ms | 0.1474 ms | 1.00 | 0.00 | 218.7500 | - | - | 757.04 KB |
+ // 'Decode Jpeg - ImageSharp' | Jpg/baseline/jpeg420exif.jpg | 41.366 ms | 1.0115 ms | 0.0572 ms | 2.42 | 0.02 | - | - | - | 21.94 KB |
+ // | | | | | | | | | | |
+ // 'Decode Jpeg - System.Drawing' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 428.282 ms | 94.9163 ms | 5.3629 ms | 1.00 | 0.00 | 2375.0000 | - | - | 7403.76 KB |
+ // 'Decode Jpeg - ImageSharp' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 386.698 ms | 33.0065 ms | 1.8649 ms | 0.90 | 0.01 | 125.0000 | 125.0000 | 125.0000 | 35186.97 KB |
+ // | | | | | | | | | | |
+ // 'Decode Jpeg - System.Drawing' | Jpg/issues/issue750-exif-tranform.jpg | 95.192 ms | 3.1762 ms | 0.1795 ms | 1.00 | 0.00 | 1750.0000 | - | - | 5492.63 KB |
+ // 'Decode Jpeg - ImageSharp' | Jpg/issues/issue750-exif-tranform.jpg | 230.158 ms | 48.8128 ms | 2.7580 ms | 2.42 | 0.02 | 312.5000 | 312.5000 | 312.5000 | 58834.66 KB |
+ }
+}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs
deleted file mode 100644
index 77ed828ef1..0000000000
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using BenchmarkDotNet.Attributes;
-using System;
-using System.IO;
-using SixLabors.ImageSharp.Tests;
-using System.Drawing;
-using System.Drawing.Drawing2D;
-using System.Drawing.Imaging;
-using SixLabors.ImageSharp.Processing;
-using SDImage = System.Drawing.Image;
-using SixLabors.ImageSharp.Formats.Jpeg;
-
-namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
-{
- [Config(typeof(Config.ShortClr))]
- public class LoadResizeSave : BenchmarkBase
- {
- private readonly Configuration configuration = new Configuration(new JpegConfigurationModule());
-
- private byte[] sourceBytes;
-
- private byte[] destBytes;
-
- private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
-
- [Params(
- TestImages.Jpeg.Baseline.Jpeg420Exif
- //, TestImages.Jpeg.Baseline.Calliphora
- )]
- public string TestImage { get; set; }
-
- [Params(false, true)]
- public bool EnableParallelExecution { get; set; }
-
- [GlobalSetup]
- public void Setup()
- {
- this.configuration.MaxDegreeOfParallelism =
- this.EnableParallelExecution ? Environment.ProcessorCount : 1;
-
- if (this.sourceBytes == null)
- {
- this.sourceBytes = File.ReadAllBytes(this.TestImageFullPath);
- }
-
- if (this.destBytes == null)
- {
- this.destBytes = new byte[this.sourceBytes.Length];
- }
- }
-
- [Benchmark(Baseline = true)]
- public void SystemDrawing()
- {
- using (var sourceStream = new MemoryStream(this.sourceBytes))
- using (var destStream = new MemoryStream(this.destBytes))
- using (var source = SDImage.FromStream(sourceStream))
- using (var destination = new Bitmap(source.Width / 4, source.Height / 4))
- {
- using (var graphics = Graphics.FromImage(destination))
- {
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
- graphics.CompositingQuality = CompositingQuality.HighQuality;
- graphics.DrawImage(source, 0, 0, 400, 400);
- }
-
- destination.Save(destStream, ImageFormat.Jpeg);
- }
- }
-
- [Benchmark]
- public void ImageSharp()
- {
- var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true });
- using (source)
- using (var destStream = new MemoryStream(this.destBytes))
- {
- source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4));
- source.SaveAsJpeg(destStream);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_Aggregate.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_Aggregate.cs
new file mode 100644
index 0000000000..e39cfa6ba2
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_Aggregate.cs
@@ -0,0 +1,96 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.IO;
+
+using BenchmarkDotNet.Attributes;
+
+using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using SixLabors.ImageSharp.Tests;
+// ReSharper disable InconsistentNaming
+
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
+{
+ [Config(typeof(MultiImageBenchmarkBase.Config))]
+ public class LoadResizeSave_Aggregate : MultiImageBenchmarkBase
+ {
+ protected override IEnumerable InputImageSubfoldersOrFiles =>
+ new[]
+ {
+ TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome,
+ TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr,
+ TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
+ TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr,
+ TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr,
+ };
+
+ [Params(InputImageCategory.AllImages)]
+ public override InputImageCategory InputCategory { get; set; }
+
+ private readonly Configuration configuration = new Configuration(new JpegConfigurationModule());
+
+ private byte[] destBytes;
+
+ public override void Setup()
+ {
+ base.Setup();
+
+ this.configuration.MaxDegreeOfParallelism = 1;
+ const int MaxOutputSizeInBytes = 2 * 1024 * 1024; // ~2 MB
+ this.destBytes = new byte[MaxOutputSizeInBytes];
+ }
+
+ [Benchmark(Baseline = true)]
+ public void SystemDrawing()
+ {
+ this.ForEachStream(
+ sourceStream =>
+ {
+ using (var destStream = new MemoryStream(this.destBytes))
+ using (var source = System.Drawing.Image.FromStream(sourceStream))
+ using (var destination = new Bitmap(source.Width / 4, source.Height / 4))
+ {
+ using (var g = Graphics.FromImage(destination))
+ {
+ g.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ g.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ g.CompositingQuality = CompositingQuality.HighQuality;
+ g.DrawImage(source, 0, 0, 400, 400);
+ }
+
+ destination.Save(destStream, ImageFormat.Jpeg);
+ }
+
+ return null;
+ });
+ }
+
+ [Benchmark]
+ public void ImageSharp()
+ {
+ this.ForEachStream(
+ sourceStream =>
+ {
+ using (var source = Image.Load(
+ this.configuration,
+ sourceStream,
+ new JpegDecoder { IgnoreMetadata = true }))
+ {
+ using (var destStream = new MemoryStream(this.destBytes))
+ {
+ source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4));
+ source.SaveAsJpeg(destStream);
+ }
+ }
+
+ return null;
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_ImageSpecific.cs
new file mode 100644
index 0000000000..1834f77eaf
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_ImageSpecific.cs
@@ -0,0 +1,107 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using BenchmarkDotNet.Attributes;
+using System;
+using System.IO;
+using SixLabors.ImageSharp.Tests;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using SixLabors.ImageSharp.Processing;
+using SDImage = System.Drawing.Image;
+using SixLabors.ImageSharp.Formats.Jpeg;
+// ReSharper disable InconsistentNaming
+
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
+{
+ [Config(typeof(Config.ShortClr))]
+ public class LoadResizeSave_ImageSpecific
+ {
+ private readonly Configuration configuration = new Configuration(new JpegConfigurationModule());
+
+ private byte[] sourceBytes;
+
+ private byte[] destBytes;
+
+ private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
+
+ [Params(
+ TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
+ TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr,
+
+ TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr
+ )]
+ public string TestImage { get; set; }
+
+ [Params(false, true)]
+ public bool ParallelExec { get; set; }
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ this.configuration.MaxDegreeOfParallelism =
+ this.ParallelExec ? Environment.ProcessorCount : 1;
+
+ this.sourceBytes = File.ReadAllBytes(this.TestImageFullPath);
+
+ this.destBytes = new byte[this.sourceBytes.Length * 2];
+ }
+
+ [Benchmark(Baseline = true)]
+ public void SystemDrawing()
+ {
+ using (var sourceStream = new MemoryStream(this.sourceBytes))
+ using (var destStream = new MemoryStream(this.destBytes))
+ using (var source = SDImage.FromStream(sourceStream))
+ using (var destination = new Bitmap(source.Width / 4, source.Height / 4))
+ {
+ using (var g = Graphics.FromImage(destination))
+ {
+ g.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ g.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ g.CompositingQuality = CompositingQuality.HighQuality;
+ g.DrawImage(source, 0, 0, 400, 400);
+ }
+
+ destination.Save(destStream, ImageFormat.Jpeg);
+ }
+ }
+
+ [Benchmark]
+ public void ImageSharp()
+ {
+ var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true });
+ using (source)
+ using (var destStream = new MemoryStream(this.destBytes))
+ {
+ source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4));
+ source.SaveAsJpeg(destStream);
+ }
+ }
+
+ // RESULTS (2018 October 31):
+ //
+ // BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
+ // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
+ // Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
+ // .NET Core SDK=2.1.403
+ // [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
+ // Job-ZPEZGV : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
+ // Job-SGOCJT : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
+ //
+ // Method | Runtime | TestImage | ParallelExec | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated |
+ // -------------- |-------- |----------------------------- |------------- |----------:|----------:|----------:|-------:|---------:|---------:|----------:|
+ // SystemDrawing | Clr | Jpg/baseline/jpeg420exif.jpg | False | 64.88 ms | 3.735 ms | 0.2110 ms | 1.00 | 0.00 | 250.0000 | 791.07 KB |
+ // ImageSharp | Clr | Jpg/baseline/jpeg420exif.jpg | False | 129.53 ms | 23.423 ms | 1.3234 ms | 2.00 | 0.02 | - | 50.09 KB |
+ // | | | | | | | | | | |
+ // SystemDrawing | Core | Jpg/baseline/jpeg420exif.jpg | False | 65.87 ms | 10.488 ms | 0.5926 ms | 1.00 | 0.00 | 250.0000 | 789.79 KB |
+ // ImageSharp | Core | Jpg/baseline/jpeg420exif.jpg | False | 92.00 ms | 7.241 ms | 0.4091 ms | 1.40 | 0.01 | - | 46.36 KB |
+ // | | | | | | | | | | |
+ // SystemDrawing | Clr | Jpg/baseline/jpeg420exif.jpg | True | 64.23 ms | 5.998 ms | 0.3389 ms | 1.00 | 0.00 | 250.0000 | 791.07 KB |
+ // ImageSharp | Clr | Jpg/baseline/jpeg420exif.jpg | True | 82.63 ms | 29.320 ms | 1.6566 ms | 1.29 | 0.02 | - | 57.59 KB |
+ // | | | | | | | | | | |
+ // SystemDrawing | Core | Jpg/baseline/jpeg420exif.jpg | True | 64.20 ms | 6.560 ms | 0.3707 ms | 1.00 | 0.00 | 250.0000 | 789.79 KB |
+ // ImageSharp | Core | Jpg/baseline/jpeg420exif.jpg | True | 68.08 ms | 18.376 ms | 1.0383 ms | 1.06 | 0.01 | - | 50.49 KB |
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs
index f046f3033b..446c038596 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs
@@ -3,6 +3,9 @@
// Licensed under the Apache License, Version 2.0.
//
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.Jobs;
+
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Benchmarks.Codecs
@@ -20,8 +23,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
using CoreImage = ImageSharp.Image;
- public abstract class MultiImageBenchmarkBase : BenchmarkBase
+ public abstract class MultiImageBenchmarkBase
{
+ public class Config : ManualConfig
+ {
+ public Config()
+ {
+ // Uncomment if you want to use any of the diagnoser
+ this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser());
+ }
+
+ public class ShortClr : Benchmarks.Config
+ {
+ public ShortClr()
+ {
+ this.Add(
+ Job.Core.WithLaunchCount(1).WithWarmupCount(1).WithTargetCount(2)
+ );
+ }
+ }
+ }
+
protected Dictionary FileNamesToBytes = new Dictionary();
protected Dictionary> FileNamesToImageSharpImages = new Dictionary>();
@@ -49,7 +71,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
///
/// Gets the file names containing these strings are substrings are not processed by the benchmark.
///
- protected IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" };
+ protected virtual IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" };
///
/// Enumerates folders containing files OR files to be processed by the benchmark.
@@ -87,7 +109,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
protected abstract IEnumerable InputImageSubfoldersOrFiles { get; }
[GlobalSetup]
- public void ReadImages()
+ public virtual void Setup()
{
if (!Vector.IsHardwareAccelerated)
{
@@ -107,11 +129,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
continue;
}
+ string[] excludeStrings = this.ExcludeSubstringsInFileNames.Select(s => s.ToLower()).ToArray();
+
string[] allFiles =
this.SearchPatterns.SelectMany(
f =>
Directory.EnumerateFiles(path, f, SearchOption.AllDirectories)
- .Where(fn => !this.ExcludeSubstringsInFileNames.Any(w => fn.ToLower().Contains(w)))).ToArray();
+ .Where(fn => !excludeStrings.Any(excludeStr => fn.ToLower().Contains(excludeStr)))).ToArray();
foreach (string fn in allFiles)
{
diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs
index f53061d4e1..148b253281 100644
--- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs
+++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs
@@ -22,15 +22,17 @@ namespace SixLabors.ImageSharp.Benchmarks
private Bitmap sourceBitmap;
- public const int SourceSize = 3032;
+ [Params(3032)]
+ public int SourceSize { get; set; }
- public const int DestSize = 400;
+ [Params(400)]
+ public int DestSize { get; set; }
[GlobalSetup]
public void Setup()
{
- this.sourceImage = new Image(this.Configuration, SourceSize, SourceSize);
- this.sourceBitmap = new Bitmap(SourceSize, SourceSize);
+ this.sourceImage = new Image(this.Configuration, this.SourceSize, this.SourceSize);
+ this.sourceBitmap = new Bitmap(this.SourceSize, this.SourceSize);
}
[GlobalCleanup]
@@ -43,14 +45,17 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Baseline = true)]
public int SystemDrawing()
{
- using (var destination = new Bitmap(DestSize, DestSize))
+ using (var destination = new Bitmap(this.DestSize, this.DestSize))
{
- using (var graphics = Graphics.FromImage(destination))
+ using (var g = Graphics.FromImage(destination))
{
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
- graphics.CompositingQuality = CompositingQuality.HighQuality;
- graphics.DrawImage(this.sourceBitmap, 0, 0, DestSize, DestSize);
+ g.CompositingMode = CompositingMode.SourceCopy;
+ g.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ g.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ g.CompositingQuality = CompositingQuality.HighQuality;
+ g.SmoothingMode = SmoothingMode.HighQuality;
+
+ g.DrawImage(this.sourceBitmap, 0, 0, this.DestSize, this.DestSize);
}
return destination.Width;
@@ -83,15 +88,60 @@ namespace SixLabors.ImageSharp.Benchmarks
{
protected override void ExecuteResizeOperation(IImageProcessingContext ctx)
{
- ctx.Resize(DestSize, DestSize, KnownResamplers.Bicubic);
+ ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic);
}
+
+ // RESULTS (2018 October):
+ //
+ // BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
+ // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
+ // Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
+ // .NET Core SDK=2.1.403
+ // [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
+ // Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
+ // Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
+ //
+ // Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated |
+ // ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:|
+ // SystemDrawing | Clr | 3032 | 400 | 101.13 ms | 18.659 ms | 1.0542 ms | 1.00 | 0.00 | 0 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 122.05 ms | 19.622 ms | 1.1087 ms | 1.21 | 0.01 | 21856 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 41.34 ms | 54.841 ms | 3.0986 ms | 0.41 | 0.03 | 28000 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 31.68 ms | 12.782 ms | 0.7222 ms | 0.31 | 0.01 | 28256 B |
+ // | | | | | | | | | |
+ // SystemDrawing | Core | 3032 | 400 | 100.37 ms | 18.479 ms | 1.0441 ms | 1.00 | 0.00 | 0 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 73.03 ms | 10.540 ms | 0.5955 ms | 0.73 | 0.01 | 21368 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 22.59 ms | 4.863 ms | 0.2748 ms | 0.23 | 0.00 | 25220 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 21.10 ms | 23.362 ms | 1.3200 ms | 0.21 | 0.01 | 25539 B |
+
}
public class Resize_BicubicCompand : ResizeBenchmarkBase
{
protected override void ExecuteResizeOperation(IImageProcessingContext ctx)
{
- ctx.Resize(DestSize, DestSize, KnownResamplers.Bicubic, true);
+ ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true);
}
+
+ // RESULTS (2018 October):
+ //
+ // BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
+ // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
+ // Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
+ // .NET Core SDK=2.1.403
+ // [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
+ // Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
+ // Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
+ //
+ // Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated |
+ // ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:|
+ // SystemDrawing | Clr | 3032 | 400 | 100.63 ms | 13.864 ms | 0.7833 ms | 1.00 | 0.00 | 0 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 156.83 ms | 28.631 ms | 1.6177 ms | 1.56 | 0.02 | 21856 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 53.43 ms | 38.493 ms | 2.1749 ms | 0.53 | 0.02 | 28512 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 38.47 ms | 11.969 ms | 0.6763 ms | 0.38 | 0.01 | 28000 B |
+ // | | | | | | | | | |
+ // SystemDrawing | Core | 3032 | 400 | 99.87 ms | 23.459 ms | 1.3255 ms | 1.00 | 0.00 | 0 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 108.19 ms | 38.562 ms | 2.1788 ms | 1.08 | 0.02 | 21368 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 36.21 ms | 53.802 ms | 3.0399 ms | 0.36 | 0.03 | 25300 B |
+ // 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 26.52 ms | 2.173 ms | 0.1228 ms | 0.27 | 0.00 | 25589 B |
}
}
diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs
index 3a3a7d31cd..c0bb25a1b9 100644
--- a/tests/ImageSharp.Sandbox46/Program.cs
+++ b/tests/ImageSharp.Sandbox46/Program.cs
@@ -4,6 +4,7 @@
//
using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations;
+using SixLabors.ImageSharp.Tests.ProfilingBenchmarks;
namespace SixLabors.ImageSharp.Sandbox46
{
@@ -62,8 +63,8 @@ namespace SixLabors.ImageSharp.Sandbox46
private static void RunDecodeJpegProfilingTests()
{
Console.WriteLine("RunDecodeJpegProfilingTests...");
- var benchmarks = new JpegProfilingBenchmarks(new ConsoleOutput());
- foreach (object[] data in JpegProfilingBenchmarks.DecodeJpegData)
+ var benchmarks = new JpegBenchmarks(new ConsoleOutput());
+ foreach (object[] data in JpegBenchmarks.DecodeJpegData)
{
string fileName = (string)data[0];
benchmarks.DecodeJpeg(fileName);
diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs
index c63cb3438f..4f8a2cdaf7 100644
--- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs
+++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs
@@ -257,6 +257,27 @@ namespace SixLabors.ImageSharp.Tests.Common
);
}
+ [Theory]
+ [InlineData(1234)]
+ public void ExtendedIntrinsics_ConvertToSingle(short scale)
+ {
+ int n = Vector.Count;
+ short[] sData = new Random(scale).GenerateRandomInt16Array(2 * n, (short)-scale, scale);
+ float[] fData = sData.Select(u => (float)u).ToArray();
+
+ var source = new Vector