Browse Source

Block8x8: the UInt16 Jpeg block

af/merge-core
Anton Firszov 9 years ago
parent
commit
d8ea09115b
  1. 97
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  2. 68
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  3. 1
      src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs
  4. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs
  5. 50
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  6. 80
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs
  7. 14
      tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs
  8. 2
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs

97
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;
/// <summary>
/// Represents a Jpeg block with <see cref="short"/> coefficiens.
/// </summary>
// ReSharper disable once InconsistentNaming
internal unsafe struct Block8x8
{
/// <summary>
/// A number of scalar coefficients in a <see cref="Block8x8F"/>
/// </summary>
public const int Size = 64;
private fixed short data[Size];
public Block8x8(Span<short> coefficients)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
ref byte sourceRef = ref coefficients.NonPortableCast<short, byte>().DangerousGetPinnableReference();
Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short));
}
/// <summary>
/// Pointer-based "Indexer" (getter part)
/// </summary>
/// <param name="blockPtr">Block pointer</param>
/// <param name="idx">Index</param>
/// <returns>The scaleVec value at the specified index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe short GetScalarAt(Block8x8* blockPtr, int idx)
{
GuardBlockIndex(idx);
short* fp = (short*)blockPtr;
return fp[idx];
}
/// <summary>
/// Pointer-based "Indexer" (setter part)
/// </summary>
/// <param name="blockPtr">Block pointer</param>
/// <param name="idx">Index</param>
/// <param name="value">Value</param>
[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<Block8x8, short>(ref this);
return Unsafe.Add(ref selfRef, idx);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
GuardBlockIndex(idx);
ref short selfRef = ref Unsafe.As<Block8x8, short>(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;
}
}
}

68
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Diagnostics;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -9,10 +10,8 @@ using System.Runtime.InteropServices;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
using SixLabors.ImageSharp.Memory;
/// <summary> /// <summary>
/// DCT code Ported from https://github.com/norishigefukushima/dct_simd /// Represents a Jpeg block with <see cref="float"/> coefficients.
/// </summary> /// </summary>
internal partial struct Block8x8F internal partial struct Block8x8F
{ {
@ -27,9 +26,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public const int VectorCount = 16; public const int VectorCount = 16;
/// <summary> /// <summary>
/// Scalar count /// A number of scalar coefficients in a <see cref="Block8x8F"/>
/// </summary> /// </summary>
public const int ScalarCount = VectorCount * 4; public const int Size = 64;
#pragma warning disable SA1600 // ElementsMustBeDocumented #pragma warning disable SA1600 // ElementsMustBeDocumented
public Vector4 V0L; public Vector4 V0L;
@ -65,29 +64,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// </summary> /// </summary>
/// <param name="idx">The index</param> /// <param name="idx">The index</param>
/// <returns>The float value at the specified index</returns> /// <returns>The float value at the specified index</returns>
public unsafe float this[int idx] public float this[int idx]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get
{ {
fixed (Block8x8F* p = &this) GuardBlockIndex(idx);
{ ref float selfRef = ref Unsafe.As<Block8x8F, float>(ref this);
float* fp = (float*)p; return Unsafe.Add(ref selfRef, idx);
return fp[idx];
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
set set
{ {
fixed (Block8x8F* p = &this) GuardBlockIndex(idx);
{ ref float selfRef = ref Unsafe.As<Block8x8F, float>(ref this);
float* fp = (float*)p; Unsafe.Add(ref selfRef, idx) = value;
fp[idx] = value;
}
} }
} }
/// <summary> /// <summary>
/// Pointer-based "Indexer" (getter part) /// Pointer-based "Indexer" (getter part)
/// </summary> /// </summary>
@ -97,6 +92,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx) public static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx)
{ {
GuardBlockIndex(idx);
float* fp = (float*)blockPtr; float* fp = (float*)blockPtr;
return fp[idx]; return fp[idx];
} }
@ -110,10 +107,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value) public static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value)
{ {
GuardBlockIndex(idx);
float* fp = (float*)blockPtr; float* fp = (float*)blockPtr;
fp[idx] = value; 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;
}
/// <summary> /// <summary>
/// Fill the block with defaults (zeroes) /// Fill the block with defaults (zeroes)
/// </summary> /// </summary>
@ -134,7 +145,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
ref byte s = ref Unsafe.As<float, byte>(ref source.DangerousGetPinnableReference()); ref byte s = ref Unsafe.As<float, byte>(ref source.DangerousGetPinnableReference());
ref byte d = ref Unsafe.As<Block8x8F, byte>(ref this); ref byte d = ref Unsafe.As<Block8x8F, byte>(ref this);
Unsafe.CopyBlock(ref d, ref s, ScalarCount * sizeof(float)); Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float));
} }
/// <summary> /// <summary>
@ -157,7 +168,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
fixed (Vector4* ptr = &this.V0L) fixed (Vector4* ptr = &this.V0L)
{ {
float* fp = (float*)ptr; float* fp = (float*)ptr;
for (int i = 0; i < ScalarCount; i++) for (int i = 0; i < Size; i++)
{ {
fp[i] = source[i]; fp[i] = source[i];
} }
@ -174,7 +185,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
ref byte d = ref Unsafe.As<float, byte>(ref dest.DangerousGetPinnableReference()); ref byte d = ref Unsafe.As<float, byte>(ref dest.DangerousGetPinnableReference());
ref byte s = ref Unsafe.As<Block8x8F, byte>(ref this); ref byte s = ref Unsafe.As<Block8x8F, byte>(ref this);
Unsafe.CopyBlock(ref d, ref s, ScalarCount * sizeof(float)); Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float));
} }
/// <summary> /// <summary>
@ -186,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public static unsafe void CopyTo(Block8x8F* blockPtr, Span<byte> dest) public static unsafe void CopyTo(Block8x8F* blockPtr, Span<byte> dest)
{ {
float* fPtr = (float*)blockPtr; float* fPtr = (float*)blockPtr;
for (int i = 0; i < ScalarCount; i++) for (int i = 0; i < Size; i++)
{ {
dest[i] = (byte)*fPtr; dest[i] = (byte)*fPtr;
fPtr++; fPtr++;
@ -213,7 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
fixed (void* ptr = &this.V0L) 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) fixed (Vector4* ptr = &this.V0L)
{ {
float* fp = (float*)ptr; float* fp = (float*)ptr;
for (int i = 0; i < ScalarCount; i++) for (int i = 0; i < Size; i++)
{ {
dest[i] = (int)fp[i]; dest[i] = (int)fp[i];
} }
@ -294,7 +305,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
float* b = (float*)blockPtr; float* b = (float*)blockPtr;
float* qtp = (float*)qtPtr; float* qtp = (float*)qtPtr;
for (int zig = 0; zig < ScalarCount; zig++) for (int zig = 0; zig < Size; zig++)
{ {
float* unzigPos = b + unzigPtr[zig]; float* unzigPos = b + unzigPtr[zig];
float val = *unzigPos; float val = *unzigPos;
@ -347,7 +358,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
float* s = (float*)block; float* s = (float*)block;
float* d = (float*)dest; float* d = (float*)dest;
for (int zig = 0; zig < ScalarCount; zig++) for (int zig = 0; zig < Size; zig++)
{ {
d[zig] = s[unzigPtr[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) // AlmostRound(dividend/divisor) = dividend/divisior + 0.5*sign(dividend)
return (dividend / divisor) + (sign * Offset); 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));
}
} }
} }

1
src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs

@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
/// <summary> /// <summary>
/// TODO: This should be simply just a <see cref="Block8x8"/>!
/// Holds the Jpeg UnZig array in a value/stack type. /// Holds the Jpeg UnZig array in a value/stack type.
/// Unzig maps from the zigzag ordering to the natural ordering. For example, /// 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 /// unzig[3] is the column and row of the fourth element in zigzag order. The

2
src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs

@ -14,6 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components
/// <summary> /// <summary>
/// The value-type <see cref="float"/> buffer sized for 4 <see cref="Common.Block8x8F"/> instances. /// The value-type <see cref="float"/> buffer sized for 4 <see cref="Common.Block8x8F"/> instances.
/// </summary> /// </summary>
public fixed float Data[4 * Block8x8F.ScalarCount]; public fixed float Data[4 * Block8x8F.Size];
} }
} }

50
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -43,13 +43,13 @@ namespace SixLabors.ImageSharp.Tests
{ {
Block8x8F block = new Block8x8F(); Block8x8F block = new Block8x8F();
for (int i = 0; i < Block8x8F.ScalarCount; i++) for (int i = 0; i < Block8x8F.Size; i++)
{ {
block[i] = i; block[i] = i;
} }
sum = 0; sum = 0;
for (int i = 0; i < Block8x8F.ScalarCount; i++) for (int i = 0; i < Block8x8F.Size; i++)
{ {
sum += block[i]; sum += block[i];
} }
@ -67,13 +67,13 @@ namespace SixLabors.ImageSharp.Tests
{ {
Block8x8F block = new Block8x8F(); 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); Block8x8F.SetScalarAt(&block, i, i);
} }
sum = 0; sum = 0;
for (int i = 0; i < Block8x8F.ScalarCount; i++) for (int i = 0; i < Block8x8F.Size; i++)
{ {
sum += Block8x8F.GetScalarAt(&block, i); sum += Block8x8F.GetScalarAt(&block, i);
} }
@ -92,13 +92,13 @@ namespace SixLabors.ImageSharp.Tests
{ {
// Block8x8F block = new Block8x8F(); // Block8x8F block = new Block8x8F();
float[] block = new float[64]; float[] block = new float[64];
for (int i = 0; i < Block8x8F.ScalarCount; i++) for (int i = 0; i < Block8x8F.Size; i++)
{ {
block[i] = i; block[i] = i;
} }
sum = 0; sum = 0;
for (int i = 0; i < Block8x8F.ScalarCount; i++) for (int i = 0; i < Block8x8F.Size; i++)
{ {
sum += block[i]; sum += block[i];
} }
@ -109,10 +109,10 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void Load_Store_FloatArray() public void Load_Store_FloatArray()
{ {
float[] data = new float[Block8x8F.ScalarCount]; float[] data = new float[Block8x8F.Size];
float[] mirror = new float[Block8x8F.ScalarCount]; 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; data[i] = i;
} }
@ -134,10 +134,10 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public unsafe void Load_Store_FloatArray_Ptr() public unsafe void Load_Store_FloatArray_Ptr()
{ {
float[] data = new float[Block8x8F.ScalarCount]; float[] data = new float[Block8x8F.Size];
float[] mirror = new float[Block8x8F.ScalarCount]; 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; data[i] = i;
} }
@ -159,10 +159,10 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void Load_Store_IntArray() public void Load_Store_IntArray()
{ {
int[] data = new int[Block8x8F.ScalarCount]; int[] data = new int[Block8x8F.Size];
int[] mirror = new int[Block8x8F.ScalarCount]; 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; data[i] = i;
} }
@ -448,14 +448,14 @@ namespace SixLabors.ImageSharp.Tests
UnzigData unzig = UnzigData.Create(); UnzigData unzig = UnzigData.Create();
int* expectedResults = stackalloc int[Block8x8F.ScalarCount]; int* expectedResults = stackalloc int[Block8x8F.Size];
ReferenceImplementations.UnZigDivRoundRational(&block, expectedResults, &qt, unzig.Data); ReferenceImplementations.UnZigDivRoundRational(&block, expectedResults, &qt, unzig.Data);
Block8x8F actualResults = default(Block8x8F); Block8x8F actualResults = default(Block8x8F);
Block8x8F.UnzigDivRound(&block, &actualResults, &qt, unzig.Data); 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 expected = expectedResults[i];
int actual = (int)actualResults[i]; int actual = (int)actualResults[i];
@ -463,5 +463,21 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(expected, actual); 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]);
}
}
} }
} }

80
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]);
}
}
}
}

14
tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs

@ -45,6 +45,20 @@ namespace SixLabors.ImageSharp.Tests
return result; 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 // ReSharper disable once InconsistentNaming
public static int[] Create8x8RandomIntData(int minValue, int maxValue, int seed = 42) public static int[] Create8x8RandomIntData(int minValue, int maxValue, int seed = 42)
{ {

2
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs

@ -888,7 +888,7 @@ namespace SixLabors.ImageSharp.Tests
float* s = (float*)src; float* s = (float*)src;
float* q = (float*)qt; 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 a = (int)s[unzigPtr[zig]];
int b = (int)q[zig]; int b = (int)q[zig];

Loading…
Cancel
Save