diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
new file mode 100644
index 000000000..a9ad4e9dd
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Common
+{
+ using System.Diagnostics;
+
+ ///
+ /// Represents a Jpeg block with coefficiens.
+ ///
+ // ReSharper disable once InconsistentNaming
+ internal unsafe struct Block8x8
+ {
+ ///
+ /// A number of scalar coefficients in a
+ ///
+ public const int Size = 64;
+
+ private fixed short data[Size];
+
+ public Block8x8(Span coefficients)
+ {
+ ref byte selfRef = ref Unsafe.As(ref this);
+ ref byte sourceRef = ref coefficients.NonPortableCast().DangerousGetPinnableReference();
+ Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short));
+ }
+
+ ///
+ /// Pointer-based "Indexer" (getter part)
+ ///
+ /// Block pointer
+ /// Index
+ /// The scaleVec value at the specified index
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe short GetScalarAt(Block8x8* blockPtr, int idx)
+ {
+ GuardBlockIndex(idx);
+
+ short* fp = (short*)blockPtr;
+ return fp[idx];
+ }
+
+ ///
+ /// Pointer-based "Indexer" (setter part)
+ ///
+ /// Block pointer
+ /// Index
+ /// Value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe void SetScalarAt(Block8x8* blockPtr, int idx, short value)
+ {
+ GuardBlockIndex(idx);
+
+ short* fp = (short*)blockPtr;
+ fp[idx] = value;
+ }
+
+
+ public short this[int idx]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ GuardBlockIndex(idx);
+ ref short selfRef = ref Unsafe.As(ref this);
+ return Unsafe.Add(ref selfRef, idx);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ GuardBlockIndex(idx);
+ ref short selfRef = ref Unsafe.As(ref this);
+ Unsafe.Add(ref selfRef, idx) = value;
+ }
+ }
+
+ [Conditional("DEBUG")]
+ private static void GuardBlockIndex(int idx)
+ {
+ DebugGuard.MustBeLessThan(idx, Size, nameof(idx));
+ DebugGuard.MustBeGreaterThanOrEqualTo(idx, 0, nameof(idx));
+ }
+
+ public Block8x8F AsFloatBlock()
+ {
+ // TODO: Optimize this
+ var result = default(Block8x8F);
+ for (int i = 0; i < Size; i++)
+ {
+ result[i] = this[i];
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
index 9ee5cfc70..491908f92 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -9,10 +10,8 @@ using System.Runtime.InteropServices;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
- using SixLabors.ImageSharp.Memory;
-
///
- /// DCT code Ported from https://github.com/norishigefukushima/dct_simd
+ /// Represents a Jpeg block with coefficients.
///
internal partial struct Block8x8F
{
@@ -27,9 +26,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public const int VectorCount = 16;
///
- /// Scalar count
+ /// A number of scalar coefficients in a
///
- public const int ScalarCount = VectorCount * 4;
+ public const int Size = 64;
#pragma warning disable SA1600 // ElementsMustBeDocumented
public Vector4 V0L;
@@ -65,29 +64,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
///
/// The index
/// The float value at the specified index
- public unsafe float this[int idx]
+ public float this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
- fixed (Block8x8F* p = &this)
- {
- float* fp = (float*)p;
- return fp[idx];
- }
+ GuardBlockIndex(idx);
+ ref float selfRef = ref Unsafe.As(ref this);
+ return Unsafe.Add(ref selfRef, idx);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
- fixed (Block8x8F* p = &this)
- {
- float* fp = (float*)p;
- fp[idx] = value;
- }
+ GuardBlockIndex(idx);
+ ref float selfRef = ref Unsafe.As(ref this);
+ Unsafe.Add(ref selfRef, idx) = value;
}
}
-
+
///
/// Pointer-based "Indexer" (getter part)
///
@@ -97,6 +92,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx)
{
+ GuardBlockIndex(idx);
+
float* fp = (float*)blockPtr;
return fp[idx];
}
@@ -110,10 +107,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value)
{
+ GuardBlockIndex(idx);
+
float* fp = (float*)blockPtr;
fp[idx] = value;
}
+ public Block8x8 AsInt16Block()
+ {
+ // TODO: Optimize this
+ var result = default(Block8x8);
+ for (int i = 0; i < Size; i++)
+ {
+ result[i] = (short)this[i];
+ }
+
+ return result;
+ }
+
///
/// Fill the block with defaults (zeroes)
///
@@ -134,7 +145,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
ref byte s = ref Unsafe.As(ref source.DangerousGetPinnableReference());
ref byte d = ref Unsafe.As(ref this);
- Unsafe.CopyBlock(ref d, ref s, ScalarCount * sizeof(float));
+ Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float));
}
///
@@ -157,7 +168,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
fixed (Vector4* ptr = &this.V0L)
{
float* fp = (float*)ptr;
- for (int i = 0; i < ScalarCount; i++)
+ for (int i = 0; i < Size; i++)
{
fp[i] = source[i];
}
@@ -174,7 +185,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
ref byte d = ref Unsafe.As(ref dest.DangerousGetPinnableReference());
ref byte s = ref Unsafe.As(ref this);
- Unsafe.CopyBlock(ref d, ref s, ScalarCount * sizeof(float));
+ Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float));
}
///
@@ -186,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest)
{
float* fPtr = (float*)blockPtr;
- for (int i = 0; i < ScalarCount; i++)
+ for (int i = 0; i < Size; i++)
{
dest[i] = (byte)*fPtr;
fPtr++;
@@ -213,7 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
fixed (void* ptr = &this.V0L)
{
- Marshal.Copy((IntPtr)ptr, dest, 0, ScalarCount);
+ Marshal.Copy((IntPtr)ptr, dest, 0, Size);
}
}
@@ -226,7 +237,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
fixed (Vector4* ptr = &this.V0L)
{
float* fp = (float*)ptr;
- for (int i = 0; i < ScalarCount; i++)
+ for (int i = 0; i < Size; i++)
{
dest[i] = (int)fp[i];
}
@@ -294,7 +305,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
float* b = (float*)blockPtr;
float* qtp = (float*)qtPtr;
- for (int zig = 0; zig < ScalarCount; zig++)
+ for (int zig = 0; zig < Size; zig++)
{
float* unzigPos = b + unzigPtr[zig];
float val = *unzigPos;
@@ -347,7 +358,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
float* s = (float*)block;
float* d = (float*)dest;
- for (int zig = 0; zig < ScalarCount; zig++)
+ for (int zig = 0; zig < Size; zig++)
{
d[zig] = s[unzigPtr[zig]];
}
@@ -411,5 +422,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
// AlmostRound(dividend/divisor) = dividend/divisior + 0.5*sign(dividend)
return (dividend / divisor) + (sign * Offset);
}
+
+ [Conditional("DEBUG")]
+ private static void GuardBlockIndex(int idx)
+ {
+ DebugGuard.MustBeLessThan(idx, Size, nameof(idx));
+ DebugGuard.MustBeGreaterThanOrEqualTo(idx, 0, nameof(idx));
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs b/src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs
index aaefbb3af..e243938e3 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs
@@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
///
+ /// TODO: This should be simply just a !
/// Holds the Jpeg UnZig array in a value/stack type.
/// Unzig maps from the zigzag ordering to the natural ordering. For example,
/// unzig[3] is the column and row of the fourth element in zigzag order. The
diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs
index 4f1b8783a..6b16ea824 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs
@@ -14,6 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components
///
/// The value-type buffer sized for 4 instances.
///
- public fixed float Data[4 * Block8x8F.ScalarCount];
+ public fixed float Data[4 * Block8x8F.Size];
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
index cff46391e..a3a8d1218 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
@@ -43,13 +43,13 @@ namespace SixLabors.ImageSharp.Tests
{
Block8x8F block = new Block8x8F();
- for (int i = 0; i < Block8x8F.ScalarCount; i++)
+ for (int i = 0; i < Block8x8F.Size; i++)
{
block[i] = i;
}
sum = 0;
- for (int i = 0; i < Block8x8F.ScalarCount; i++)
+ for (int i = 0; i < Block8x8F.Size; i++)
{
sum += block[i];
}
@@ -67,13 +67,13 @@ namespace SixLabors.ImageSharp.Tests
{
Block8x8F block = new Block8x8F();
- for (int i = 0; i < Block8x8F.ScalarCount; i++)
+ for (int i = 0; i < Block8x8F.Size; i++)
{
Block8x8F.SetScalarAt(&block, i, i);
}
sum = 0;
- for (int i = 0; i < Block8x8F.ScalarCount; i++)
+ for (int i = 0; i < Block8x8F.Size; i++)
{
sum += Block8x8F.GetScalarAt(&block, i);
}
@@ -92,13 +92,13 @@ namespace SixLabors.ImageSharp.Tests
{
// Block8x8F block = new Block8x8F();
float[] block = new float[64];
- for (int i = 0; i < Block8x8F.ScalarCount; i++)
+ for (int i = 0; i < Block8x8F.Size; i++)
{
block[i] = i;
}
sum = 0;
- for (int i = 0; i < Block8x8F.ScalarCount; i++)
+ for (int i = 0; i < Block8x8F.Size; i++)
{
sum += block[i];
}
@@ -109,10 +109,10 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void Load_Store_FloatArray()
{
- float[] data = new float[Block8x8F.ScalarCount];
- float[] mirror = new float[Block8x8F.ScalarCount];
+ float[] data = new float[Block8x8F.Size];
+ float[] mirror = new float[Block8x8F.Size];
- for (int i = 0; i < Block8x8F.ScalarCount; i++)
+ for (int i = 0; i < Block8x8F.Size; i++)
{
data[i] = i;
}
@@ -134,10 +134,10 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public unsafe void Load_Store_FloatArray_Ptr()
{
- float[] data = new float[Block8x8F.ScalarCount];
- float[] mirror = new float[Block8x8F.ScalarCount];
+ float[] data = new float[Block8x8F.Size];
+ float[] mirror = new float[Block8x8F.Size];
- for (int i = 0; i < Block8x8F.ScalarCount; i++)
+ for (int i = 0; i < Block8x8F.Size; i++)
{
data[i] = i;
}
@@ -159,10 +159,10 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void Load_Store_IntArray()
{
- int[] data = new int[Block8x8F.ScalarCount];
- int[] mirror = new int[Block8x8F.ScalarCount];
+ int[] data = new int[Block8x8F.Size];
+ int[] mirror = new int[Block8x8F.Size];
- for (int i = 0; i < Block8x8F.ScalarCount; i++)
+ for (int i = 0; i < Block8x8F.Size; i++)
{
data[i] = i;
}
@@ -448,14 +448,14 @@ namespace SixLabors.ImageSharp.Tests
UnzigData unzig = UnzigData.Create();
- int* expectedResults = stackalloc int[Block8x8F.ScalarCount];
+ int* expectedResults = stackalloc int[Block8x8F.Size];
ReferenceImplementations.UnZigDivRoundRational(&block, expectedResults, &qt, unzig.Data);
Block8x8F actualResults = default(Block8x8F);
Block8x8F.UnzigDivRound(&block, &actualResults, &qt, unzig.Data);
- for (int i = 0; i < Block8x8F.ScalarCount; i++)
+ for (int i = 0; i < Block8x8F.Size; i++)
{
int expected = expectedResults[i];
int actual = (int)actualResults[i];
@@ -463,5 +463,21 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(expected, actual);
}
}
+
+ [Fact]
+ public void AsInt16Block()
+ {
+ float[] data = Create8x8FloatData();
+
+ var source = default(Block8x8F);
+ source.LoadFrom(data);
+
+ Block8x8 dest = source.AsInt16Block();
+
+ for (int i = 0; i < Block8x8F.Size; i++)
+ {
+ Assert.Equal((short)data[i], dest[i]);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs
new file mode 100644
index 000000000..46b12b80f
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs
@@ -0,0 +1,80 @@
+// ReSharper disable InconsistentNaming
+namespace SixLabors.ImageSharp.Tests
+{
+ using Moq;
+
+ using SixLabors.ImageSharp.Formats.Jpeg.Common;
+
+ using Xunit;
+ using Xunit.Abstractions;
+
+ public class Block8x8Tests : JpegUtilityTestFixture
+ {
+ public Block8x8Tests(ITestOutputHelper output)
+ : base(output)
+ {
+ }
+
+ [Fact]
+ public void Construct_And_Indexer_Get()
+ {
+ short[] data = Create8x8ShortData();
+
+ var block = new Block8x8(data);
+
+ for (int i = 0; i < Block8x8.Size; i++)
+ {
+ Assert.Equal(data[i], block[i]);
+ }
+ }
+
+ [Fact]
+ public void Indexer_Set()
+ {
+ var block = default(Block8x8);
+
+ block[17] = 17;
+ block[42] = 42;
+
+ Assert.Equal(0, block[0]);
+ Assert.Equal(17, block[17]);
+ Assert.Equal(42, block[42]);
+ }
+
+
+ [Fact]
+ public unsafe void Indexer_GetScalarAt_SetScalarAt()
+ {
+ int sum = 0;
+ var block = default(Block8x8);
+
+ for (int i = 0; i < Block8x8.Size; i++)
+ {
+ Block8x8.SetScalarAt(&block, i, i);
+ }
+
+ sum = 0;
+ for (int i = 0; i < Block8x8.Size; i++)
+ {
+ sum += Block8x8.GetScalarAt(&block, i);
+ }
+ Assert.Equal(sum, 64 * 63 / 2);
+ }
+
+
+ [Fact]
+ public void AsFloatBlock()
+ {
+ short[] data = Create8x8ShortData();
+
+ var source = new Block8x8(data);
+
+ Block8x8F dest = source.AsFloatBlock();
+
+ for (int i = 0; i < Block8x8F.Size; i++)
+ {
+ Assert.Equal((float)data[i], dest[i]);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs
index ffb3c1af8..c3ccba8f6 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs
@@ -45,6 +45,20 @@ namespace SixLabors.ImageSharp.Tests
return result;
}
+ // ReSharper disable once InconsistentNaming
+ public static short[] Create8x8ShortData()
+ {
+ short[] result = new short[64];
+ for (int i = 0; i < 8; i++)
+ {
+ for (int j = 0; j < 8; j++)
+ {
+ result[i * 8 + j] = (short)(i * 10 + j);
+ }
+ }
+ return result;
+ }
+
// ReSharper disable once InconsistentNaming
public static int[] Create8x8RandomIntData(int minValue, int maxValue, int seed = 42)
{
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
index f085c858c..e86f96572 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
@@ -888,7 +888,7 @@ namespace SixLabors.ImageSharp.Tests
float* s = (float*)src;
float* q = (float*)qt;
- for (int zig = 0; zig < Block8x8F.ScalarCount; zig++)
+ for (int zig = 0; zig < Block8x8F.Size; zig++)
{
int a = (int)s[unzigPtr[zig]];
int b = (int)q[zig];