Browse Source

ToString(), CopyTo(), ToArray() for Block8x8

af/merge-core
Anton Firszov 9 years ago
parent
commit
8e56cc4140
  1. 79
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  2. 4
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockProcessor.cs
  3. 6
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldComponent.cs
  4. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegScanDecoder.ComputationData.cs
  5. 4
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegScanDecoder.DataPointers.cs
  6. 52
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegScanDecoder.cs
  7. 10
      src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs
  8. 20
      src/ImageSharp/Formats/Jpeg/GolangPort/OldJpegDecoderCore.cs
  9. 14
      tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs
  10. 13
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs

79
src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs

@ -1,10 +1,10 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
using System.Diagnostics;
/// <summary>
/// Represents a Jpeg block with <see cref="short"/> coefficiens.
/// </summary>
@ -25,6 +25,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short));
}
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;
}
}
/// <summary>
/// Pointer-based "Indexer" (getter part)
/// </summary>
@ -32,11 +51,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <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)
public static short GetScalarAt(Block8x8* blockPtr, int idx)
{
GuardBlockIndex(idx);
short* fp = (short*)blockPtr;
short* fp = blockPtr->data;
return fp[idx];
}
@ -47,32 +66,39 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <param name="idx">Index</param>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void SetScalarAt(Block8x8* blockPtr, int idx, short value)
public static void SetScalarAt(Block8x8* blockPtr, int idx, short value)
{
GuardBlockIndex(idx);
short* fp = (short*)blockPtr;
short* fp = blockPtr->data;
fp[idx] = value;
}
public short this[int idx]
public Block8x8F AsFloatBlock()
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
// TODO: Optimize this
var result = default(Block8x8F);
for (int i = 0; i < Size; i++)
{
GuardBlockIndex(idx);
ref short selfRef = ref Unsafe.As<Block8x8, short>(ref this);
return Unsafe.Add(ref selfRef, idx);
result[i] = this[i];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
GuardBlockIndex(idx);
ref short selfRef = ref Unsafe.As<Block8x8, short>(ref this);
Unsafe.Add(ref selfRef, idx) = value;
}
return result;
}
public short[] ToArray()
{
short[] result = new short[Size];
this.CopyTo(result);
return result;
}
public void CopyTo(Span<short> destination)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
ref byte destRef = ref destination.NonPortableCast<short, byte>().DangerousGetPinnableReference();
Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short));
}
[Conditional("DEBUG")]
@ -82,16 +108,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
DebugGuard.MustBeGreaterThanOrEqualTo(idx, 0, nameof(idx));
}
public Block8x8F AsFloatBlock()
public override string ToString()
{
// TODO: Optimize this
var result = default(Block8x8F);
var bld = new StringBuilder();
bld.Append('[');
for (int i = 0; i < Size; i++)
{
result[i] = this[i];
bld.Append(this[i]);
if (i < Size - 1)
{
bld.Append(',');
}
}
return result;
bld.Append(']');
return bld.ToString();
}
}
}

4
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockProcessor.cs

@ -67,7 +67,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <param name="by">The y index of the block in <see cref="OldComponent.SpectralBlocks"/></param>
private void ProcessBlockColors(OldJpegDecoderCore decoder, OldComponent component, int bx, int by)
{
this.data.Block = component.GetBlockReference(bx, by);
ref Block8x8 sourceBlock = ref component.GetBlockReference(bx, by);
this.data.Block = sourceBlock.AsFloatBlock();
int qtIndex = decoder.Components[this.componentIndex].Selector;
this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex];

6
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldComponent.cs

@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// This is done by <see cref="OldJpegDecoderCore.ProcessBlocksIntoJpegImageChannels{TPixel}"/>.
/// When <see cref="OldJpegDecoderCore.IsProgressive"/> us true, we are touching these blocks multiple times - each time we process a Scan.
/// </summary>
public Buffer2D<Block8x8F> SpectralBlocks { get; private set; }
public Buffer2D<Block8x8> SpectralBlocks { get; private set; }
/// <summary>
/// Gets the number of blocks for this component along the X axis
@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary>
public int BlockCountY { get; private set; }
public ref Block8x8F GetBlockReference(int bx, int by)
public ref Block8x8 GetBlockReference(int bx, int by)
{
return ref this.SpectralBlocks[bx, by];
}
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{
this.BlockCountX = decoder.MCUCountX * this.HorizontalFactor;
this.BlockCountY = decoder.MCUCountY * this.VerticalFactor;
this.SpectralBlocks = Buffer2D<Block8x8F>.CreateClean(this.BlockCountX, this.BlockCountY);
this.SpectralBlocks = Buffer2D<Block8x8>.CreateClean(this.BlockCountX, this.BlockCountY);
}
/// <summary>

2
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegScanDecoder.ComputationData.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary>
/// The main input/working block
/// </summary>
public Block8x8F Block;
public Block8x8 Block;
/// <summary>
/// The jpeg unzig data

4
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegScanDecoder.DataPointers.cs

@ -5,6 +5,8 @@ using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{
using SixLabors.ImageSharp.Formats.Jpeg.Common;
/// <content>
/// Conains the definition of <see cref="DataPointers"/>
/// </content>
@ -18,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary>
/// Pointer to <see cref="ComputationData.Block"/>
/// </summary>
public Block8x8F* Block;
public Block8x8* Block;
/// <summary>
/// Pointer to <see cref="ComputationData.Unzig"/> as int*

52
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegScanDecoder.cs

@ -9,6 +9,8 @@ using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{
using SixLabors.ImageSharp.Formats.Jpeg.Common;
/// <summary>
/// Encapsulates the impementation of Jpeg SOS Huffman decoding. See JpegScanDecoder.md!
///
@ -171,7 +173,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
// Find the block at (bx,by) in the component's buffer:
OldComponent component = decoder.Components[this.ComponentIndex];
ref Block8x8F blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by);
ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by);
// Copy block to stack
this.data.Block = blockRefOnHeap;
@ -273,7 +275,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
throw new ImageFormatException("Total sampling factors too large.");
}
this.zigEnd = Block8x8F.ScalarCount - 1;
this.zigEnd = Block8x8F.Size - 1;
if (decoder.IsProgressive)
{
@ -283,7 +285,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.al = decoder.Temp[3 + scanComponentCountX2] & 0x0f;
if ((this.zigStart == 0 && this.zigEnd != 0) || this.zigStart > this.zigEnd
|| this.zigEnd >= Block8x8F.ScalarCount)
|| this.zigEnd >= Block8x8F.Size)
{
throw new ImageFormatException("Bad spectral selection bounds");
}
@ -307,7 +309,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <param name="scanIndex">The index of the scan</param>
private void DecodeBlock(OldJpegDecoderCore decoder, int scanIndex)
{
Block8x8F* b = this.pointers.Block;
Block8x8* b = this.pointers.Block;
int huffmannIdx = (OldHuffmanTree.AcTableIndex * OldHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector;
if (this.ah != 0)
{
@ -347,7 +349,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.pointers.Dc[this.ComponentIndex] += deltaDC;
// b[0] = dc[compIndex] << al;
Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[this.ComponentIndex] << this.al);
value = this.pointers.Dc[this.ComponentIndex] << this.al;
Block8x8.SetScalarAt(b, 0, (short) value);
}
if (zig <= this.zigEnd && this.eobRun > 0)
@ -384,7 +387,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
}
// b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al);
value = ac << this.al;
Block8x8.SetScalarAt(b, this.pointers.Unzig[zig], (short)value);
}
else
{
@ -501,7 +505,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <param name="delta">The low transform offset</param>
private void Refine(ref InputProcessor bp, ref OldHuffmanTree h, int delta)
{
Block8x8F* b = this.pointers.Block;
Block8x8* b = this.pointers.Block;
// Refining a DC component is trivial.
if (this.zigStart == 0)
@ -520,13 +524,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
if (bit)
{
int stuff = (int)Block8x8F.GetScalarAt(b, 0);
int stuff = (int)Block8x8.GetScalarAt(b, 0);
// int stuff = (int)b[0];
stuff |= delta;
// b[0] = stuff;
Block8x8F.SetScalarAt(b, 0, stuff);
Block8x8.SetScalarAt(b, 0, (short)stuff);
}
return;
@ -609,7 +613,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
if (z != 0)
{
// b[Unzig[zig]] = z;
Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], z);
Block8x8.SetScalarAt(b, this.pointers.Unzig[zig], (short)z);
}
}
}
@ -632,11 +636,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <returns>The <see cref="int" /></returns>
private int RefineNonZeroes(ref InputProcessor bp, int zig, int nz, int delta)
{
Block8x8F* b = this.pointers.Block;
Block8x8* b = this.pointers.Block;
for (; zig <= this.zigEnd; zig++)
{
int u = this.pointers.Unzig[zig];
float bu = Block8x8F.GetScalarAt(b, u);
int bu = Block8x8.GetScalarAt(b, u);
// TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary?
if (bu == 0)
@ -662,16 +666,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
continue;
}
if (bu >= 0)
{
// b[u] += delta;
Block8x8F.SetScalarAt(b, u, bu + delta);
}
else
{
// b[u] -= delta;
Block8x8F.SetScalarAt(b, u, bu - delta);
}
int val = bu >= 0 ? bu + delta : bu - delta;
Block8x8.SetScalarAt(b, u, (short)val);
//if (bu >= 0)
//{
// // b[u] += delta;
// Block8x8.SetScalarAt(b, u, bu + delta);
//}
//else
//{
// // b[u] -= delta;
// Block8x8.SetScalarAt(b, u, bu - delta);
//}
}
return zig;

10
src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs

@ -262,7 +262,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F quant)
{
dqt[offset++] = (byte)i;
for (int j = 0; j < Block8x8F.ScalarCount; j++)
for (int j = 0; j < Block8x8F.Size; j++)
{
dqt[offset++] = (byte)quant[j];
}
@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// <param name="quant">The quantization table.</param>
private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant)
{
for (int j = 0; j < Block8x8F.ScalarCount; j++)
for (int j = 0; j < Block8x8F.Size; j++)
{
int x = UnscaledQuant[i, j];
x = ((x * scale) + 50) / 100;
@ -576,7 +576,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
HuffIndex h = (HuffIndex)((2 * (int)index) + 1);
int runLength = 0;
for (int zig = 1; zig < Block8x8F.ScalarCount; zig++)
for (int zig = 1; zig < Block8x8F.Size; zig++)
{
int ac = (int)unziggedDestPtr[zig];
@ -660,12 +660,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
private void WriteDefineQuantizationTables()
{
// Marker + quantization table lengths
int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.ScalarCount));
int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.Size));
this.WriteMarkerHeader(OldJpegConstants.Markers.DQT, markerlen);
// Loop through and collect the tables as one array.
// This allows us to reduce the number of writes to the stream.
int dqtCount = (QuantizationTableCount * Block8x8F.ScalarCount) + QuantizationTableCount;
int dqtCount = (QuantizationTableCount * Block8x8F.Size) + QuantizationTableCount;
byte[] dqt = ArrayPool<byte>.Shared.Rent(dqtCount);
int offset = 0;

20
src/ImageSharp/Formats/Jpeg/GolangPort/OldJpegDecoderCore.cs

@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.configuration = configuration ?? Configuration.Default;
this.HuffmanTrees = OldHuffmanTree.CreateHuffmanTrees();
this.QuantizationTables = new Block8x8F[MaxTq + 1];
this.Temp = new byte[2 * Block8x8F.ScalarCount];
this.Temp = new byte[2 * Block8x8F.Size];
}
/// <summary>
@ -252,7 +252,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
}
/// <summary>
/// Read metadata from stream and read the blocks in the scans into <see cref="DecodedBlocks"/>.
/// Read metadata from stream and read the blocks in the scans into <see cref="OldComponent.SpectralBlocks"/>.
/// </summary>
/// <param name="metadata">The metadata</param>
/// <param name="stream">The stream</param>
@ -1113,32 +1113,32 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
switch (x >> 4)
{
case 0:
if (remaining < Block8x8F.ScalarCount)
if (remaining < Block8x8F.Size)
{
done = true;
break;
}
remaining -= Block8x8F.ScalarCount;
this.InputProcessor.ReadFull(this.Temp, 0, Block8x8F.ScalarCount);
remaining -= Block8x8F.Size;
this.InputProcessor.ReadFull(this.Temp, 0, Block8x8F.Size);
for (int i = 0; i < Block8x8F.ScalarCount; i++)
for (int i = 0; i < Block8x8F.Size; i++)
{
this.QuantizationTables[tq][i] = this.Temp[i];
}
break;
case 1:
if (remaining < 2 * Block8x8F.ScalarCount)
if (remaining < 2 * Block8x8F.Size)
{
done = true;
break;
}
remaining -= 2 * Block8x8F.ScalarCount;
this.InputProcessor.ReadFull(this.Temp, 0, 2 * Block8x8F.ScalarCount);
remaining -= 2 * Block8x8F.Size;
this.InputProcessor.ReadFull(this.Temp, 0, 2 * Block8x8F.Size);
for (int i = 0; i < Block8x8F.ScalarCount; i++)
for (int i = 0; i < Block8x8F.Size; i++)
{
this.QuantizationTables[tq][i] = (this.Temp[2 * i] << 8) | this.Temp[(2 * i) + 1];
}

14
tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs

@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General
[GlobalSetup]
public void Setup()
{
for (int i = 0; i < Block8x8F.ScalarCount; i++)
for (int i = 0; i < Block8x8F.Size; i++)
{
this.inputDividend[i] = i*44.8f;
this.inputDivisior[i] = 100 - i;
@ -44,18 +44,18 @@ namespace SixLabors.ImageSharp.Benchmarks.General
float* pDividend = (float*)&b1;
float* pDivisor = (float*)&b2;
int* result = stackalloc int[Block8x8F.ScalarCount];
int* result = stackalloc int[Block8x8F.Size];
for (int cnt = 0; cnt < ExecutionCount; cnt++)
{
sum = 0;
for (int i = 0; i < Block8x8F.ScalarCount; i++)
for (int i = 0; i < Block8x8F.Size; i++)
{
int a = (int) pDividend[i];
int b = (int) pDivisor;
result[i] = RationalRound(a, b);
}
for (int i = 0; i < Block8x8F.ScalarCount; i++)
for (int i = 0; i < Block8x8F.Size; i++)
{
sum += result[i];
}
@ -77,12 +77,12 @@ namespace SixLabors.ImageSharp.Benchmarks.General
for (int cnt = 0; cnt < ExecutionCount; cnt++)
{
sum = 0;
for (int i = 0; i < Block8x8F.ScalarCount; i++)
for (int i = 0; i < Block8x8F.Size; i++)
{
double value = pDividend[i] / pDivisor[i];
pDividend[i] = (float) System.Math.Round(value);
}
for (int i = 0; i < Block8x8F.ScalarCount; i++)
for (int i = 0; i < Block8x8F.Size; i++)
{
sum += (int) pDividend[i];
}
@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General
{
sum = 0;
DivideRoundAll(ref bDividend, ref bDivisor);
for (int i = 0; i < Block8x8F.ScalarCount; i++)
for (int i = 0; i < Block8x8F.Size; i++)
{
sum += (int)pDividend[i];
}

13
tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs

@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests
for (int i = 0; i < Block8x8.Size; i++)
{
Block8x8.SetScalarAt(&block, i, i);
Block8x8.SetScalarAt(&block, i, (short)i);
}
sum = 0;
@ -76,5 +76,16 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal((float)data[i], dest[i]);
}
}
[Fact]
public void ToArray()
{
short[] data = Create8x8ShortData();
var block = new Block8x8(data);
short[] result = block.ToArray();
Assert.Equal(data, result);
}
}
}
Loading…
Cancel
Save