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.
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;
/// <summary>
/// DCT code Ported from https://github.com/norishigefukushima/dct_simd
/// Represents a Jpeg block with <see cref="float"/> coefficients.
/// </summary>
internal partial struct Block8x8F
{
@ -27,9 +26,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public const int VectorCount = 16;
/// <summary>
/// Scalar count
/// A number of scalar coefficients in a <see cref="Block8x8F"/>
/// </summary>
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
/// </summary>
/// <param name="idx">The index</param>
/// <returns>The float value at the specified index</returns>
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<Block8x8F, float>(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<Block8x8F, float>(ref this);
Unsafe.Add(ref selfRef, idx) = value;
}
}
/// <summary>
/// Pointer-based "Indexer" (getter part)
/// </summary>
@ -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;
}
/// <summary>
/// Fill the block with defaults (zeroes)
/// </summary>
@ -134,7 +145,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
ref byte s = ref Unsafe.As<float, byte>(ref source.DangerousGetPinnableReference());
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>
@ -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<float, byte>(ref dest.DangerousGetPinnableReference());
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>
@ -186,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public static unsafe void CopyTo(Block8x8F* blockPtr, Span<byte> 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));
}
}
}

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

@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
/// <summary>
/// TODO: This should be simply just a <see cref="Block8x8"/>!
/// 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

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

@ -14,6 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components
/// <summary>
/// The value-type <see cref="float"/> buffer sized for 4 <see cref="Common.Block8x8F"/> instances.
/// </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();
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]);
}
}
}
}

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;
}
// 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)
{

2
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];

Loading…
Cancel
Save