diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
index b7dd125a88..50f8b61212 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
@@ -12,55 +12,32 @@ 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 the 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,10 +57,10 @@ 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;
+ ref Vector2 destBase = ref Unsafe.As(ref area.GetReferenceToOrigin());
+ int destStride = area.Stride / 2;
this.WidenCopyImpl2x2(ref destBase, 0, destStride);
this.WidenCopyImpl2x2(ref destBase, 1, destStride);
@@ -96,60 +73,75 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WidenCopyImpl2x2(ref float destBase, int row, int destStride)
+ private void WidenCopyImpl2x2(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);
+
+ 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;
}
- [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/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..89de95ee94
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs
@@ -0,0 +1,287 @@
+// 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 selfRight = 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;
+ }
+
+ // RESULTS:
+ // Method | Mean | Error | StdDev | Scaled |
+ // ------------ |---------:|----------:|----------:|-------:|
+ // Original | 88.93 ns | 0.7783 ns | 0.6899 ns | 1.00 |
+ // Original_V2 | 88.39 ns | 0.9426 ns | 0.8356 ns | 0.99 |
+ // UseVector2 | 45.63 ns | 0.4248 ns | 0.3548 ns | 0.51 |
+ }
+}
\ 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/General/Block8x8F_LoadFromInt16.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs
similarity index 83%
rename from tests/ImageSharp.Benchmarks/General/Block8x8F_LoadFromInt16.cs
rename to tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs
index 34847148bf..29ee402a00 100644
--- a/tests/ImageSharp.Benchmarks/General/Block8x8F_LoadFromInt16.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs
@@ -1,11 +1,16 @@
-using System;
+// 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.General
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
public class Block8x8F_LoadFromInt16
{
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.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs
index c720fdd4a7..3f426e232d 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs
@@ -11,10 +11,11 @@ using SixLabors.Primitives;
using Xunit;
using Xunit.Abstractions;
+// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
- public partial class Block8x8FTests : JpegFixture
+ public partial class Block8x8FTests
{
public class CopyToBufferArea : JpegFixture
{
@@ -37,17 +38,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
- // TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.
- [Fact(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")]
- //[Fact]
- public void Unscaled()
+ [Fact]
+ public void Copy1x1Scale()
{
Block8x8F block = CreateRandomFloatBlock(0, 100);
- using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D(20, 20))
+ using (Buffer2D buffer = Configuration.Default.MemoryAllocator.Allocate2D(20, 20))
{
BufferArea area = buffer.GetArea(5, 10, 8, 8);
- block.CopyTo(area);
+ block.Copy1x1Scale(area);
Assert.Equal(block[0, 0], buffer[5, 10]);
Assert.Equal(block[1, 0], buffer[6, 10]);
@@ -59,22 +58,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
- // TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.
- [Theory(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")]
- //[Theory]
+ [Theory]
[InlineData(1, 1)]
[InlineData(1, 2)]
[InlineData(2, 1)]
[InlineData(2, 2)]
[InlineData(4, 2)]
[InlineData(4, 4)]
- public void Scaled(int horizontalFactor, int verticalFactor)
+ public void CopyTo(int horizontalFactor, int verticalFactor)
{
Block8x8F block = CreateRandomFloatBlock(0, 100);
var start = new Point(50, 50);
- using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D(100, 100))
+ using (Buffer2D buffer = Configuration.Default.MemoryAllocator.Allocate2D(100, 100))
{
BufferArea area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor);
block.CopyTo(area, horizontalFactor, verticalFactor);