Browse Source

Cleanup + Comments + StyleCop

af/merge-core
Anton Firszov 10 years ago
parent
commit
0e20436f9c
  1. 35
      src/ImageSharp/Formats/Jpg/Components/Bits.cs
  2. 86
      src/ImageSharp/Formats/Jpg/Components/Block.cs
  3. 35
      src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.cs
  4. 45
      src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.tt
  5. 494
      src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
  6. 44
      src/ImageSharp/Formats/Jpg/Components/Bytes.cs
  7. 42
      src/ImageSharp/Formats/Jpg/Components/Huffman.cs
  8. 2
      src/ImageSharp/Formats/Jpg/Components/IDCT.cs
  9. 45
      src/ImageSharp/Formats/Jpg/Components/MutableSpan.cs
  10. 447
      src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
  11. 57
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  12. 199
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs

35
src/ImageSharp/Formats/Jpg/Components/Bits.cs

@ -3,10 +3,10 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System.Runtime.CompilerServices;
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System.Runtime.CompilerServices;
/// <summary> /// <summary>
/// Holds the unprocessed bits that have been taken from the byte-stream. /// Holds the unprocessed bits that have been taken from the byte-stream.
/// The n least significant bits of a form the unread bits, to be read in MSB to /// The n least significant bits of a form the unread bits, to be read in MSB to
@ -30,20 +30,20 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
public int UnreadBits; public int UnreadBits;
/// <summary> /// <summary>
/// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at /// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at
/// least n. For best performance (avoiding function calls inside hot loops), /// least n. For best performance (avoiding function calls inside hot loops),
/// the caller is the one responsible for first checking that bits.UnreadBits &lt; n. /// the caller is the one responsible for first checking that bits.UnreadBits &lt; n.
/// </summary> /// </summary>
/// <param name="n">The number of bits to ensure.</param> /// <param name="n">The number of bits to ensure.</param>
/// <param name="decoder"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal JpegDecoderCore.ErrorCodes EnsureNBits(int n, JpegDecoderCore decoder) internal JpegDecoderCore.ErrorCodes EnsureNBits(int n, JpegDecoderCore decoder)
{ {
while (true) while (true)
{ {
JpegDecoderCore.ErrorCodes errorCode; JpegDecoderCore.ErrorCodes errorCode;
byte c = decoder.bytes.ReadByteStuffedByte(decoder.inputStream, out errorCode); byte c = decoder.bytes.ReadByteStuffedByte(decoder.inputStream, out errorCode);
if (errorCode != JpegDecoderCore.ErrorCodes.NoError) if (errorCode != JpegDecoderCore.ErrorCodes.NoError)
@ -51,40 +51,39 @@ namespace ImageSharp.Formats
return errorCode; return errorCode;
} }
Accumulator = (Accumulator << 8) | c; this.Accumulator = (this.Accumulator << 8) | c;
UnreadBits += 8; this.UnreadBits += 8;
if (Mask == 0) if (this.Mask == 0)
{ {
Mask = 1 << 7; this.Mask = 1 << 7;
} }
else else
{ {
Mask <<= 8; this.Mask <<= 8;
} }
if (UnreadBits >= n) if (this.UnreadBits >= n)
{ {
return JpegDecoderCore.ErrorCodes.NoError; return JpegDecoderCore.ErrorCodes.NoError;
//break;
} }
} }
} }
internal int ReceiveExtend(byte t, JpegDecoderCore decoder) internal int ReceiveExtend(byte t, JpegDecoderCore decoder)
{ {
if (UnreadBits < t) if (this.UnreadBits < t)
{ {
var errorCode = EnsureNBits(t, decoder); var errorCode = this.EnsureNBits(t, decoder);
if (errorCode != JpegDecoderCore.ErrorCodes.NoError) if (errorCode != JpegDecoderCore.ErrorCodes.NoError)
{ {
throw new JpegDecoderCore.MissingFF00Exception(); throw new JpegDecoderCore.MissingFF00Exception();
} }
} }
UnreadBits -= t; this.UnreadBits -= t;
Mask >>= t; this.Mask >>= t;
int s = 1 << t; int s = 1 << t;
int x = (int)((Accumulator >> UnreadBits) & (s - 1)); int x = (int)((this.Accumulator >> this.UnreadBits) & (s - 1));
if (x < (s >> 1)) if (x < (s >> 1))
{ {
@ -93,7 +92,5 @@ namespace ImageSharp.Formats
return x; return x;
} }
} }
} }

86
src/ImageSharp/Formats/Jpg/Components/Block.cs

@ -2,13 +2,12 @@
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
/// <summary> /// <summary>
/// Represents an 8x8 block of coefficients to transform and encode. /// Represents an 8x8 block of coefficients to transform and encode.
/// </summary> /// </summary>
@ -26,17 +25,9 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
public int[] Data; public int[] Data;
/// <summary>
/// Initializes a new instance of the <see cref="Block"/> class.
/// </summary>
//public Block()
//{
// this.data = new int[BlockSize];
//}
public void Init() public void Init()
{ {
//this.Data = new int[BlockSize]; // this.Data = new int[BlockSize];
this.Data = ArrayPool.Rent(BlockSize); this.Data = ArrayPool.Rent(BlockSize);
} }
@ -54,6 +45,7 @@ namespace ImageSharp.Formats
{ {
result[i].Init(); result[i].Init();
} }
return result; return result;
} }
@ -69,18 +61,25 @@ namespace ImageSharp.Formats
public int this[int index] public int this[int index]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return this.Data[index]; } get
{
return this.Data[index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
set { this.Data[index] = value; } set
{
this.Data[index] = value;
}
} }
// TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement! // TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement!
public void Dispose() public void Dispose()
{ {
if (Data != null) if (this.Data != null)
{ {
ArrayPool.Return(Data, true); ArrayPool.Return(this.Data, true);
Data = null; this.Data = null;
} }
} }
@ -92,24 +91,24 @@ namespace ImageSharp.Formats
} }
} }
public void Clear() public void Clear()
{ {
for (int i = 0; i < Data.Length; i++) for (int i = 0; i < this.Data.Length; i++)
{ {
Data[i] = 0; this.Data[i] = 0;
} }
} }
public Block Clone() public Block Clone()
{ {
Block clone = Create(); Block clone = Create();
Array.Copy(Data, clone.Data, BlockSize); Array.Copy(this.Data, clone.Data, BlockSize);
return clone; return clone;
} }
} }
/// <summary> /// <summary>
/// TODO: Should be removed, when JpegEncoderCore is refactored to use Block8x8F
/// Temporal class to make refactoring easier. /// Temporal class to make refactoring easier.
/// 1. Refactor Block -> BlockF /// 1. Refactor Block -> BlockF
/// 2. Test /// 2. Test
@ -120,7 +119,7 @@ namespace ImageSharp.Formats
private static readonly ArrayPool<float> ArrayPool = ArrayPool<float>.Create(BlockSize, 50); private static readonly ArrayPool<float> ArrayPool = ArrayPool<float>.Create(BlockSize, 50);
/// <summary> /// <summary>
/// Gets the size of the block. /// Size of the block.
/// </summary> /// </summary>
public const int BlockSize = 64; public const int BlockSize = 64;
@ -129,17 +128,9 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
public float[] Data; public float[] Data;
/// <summary>
/// Initializes a new instance of the <see cref="Block"/> class.
/// </summary>
//public Block()
//{
// this.data = new int[BlockSize];
//}
public void Init() public void Init()
{ {
//this.Data = new int[BlockSize]; // this.Data = new int[BlockSize];
this.Data = ArrayPool.Rent(BlockSize); this.Data = ArrayPool.Rent(BlockSize);
} }
@ -157,6 +148,7 @@ namespace ImageSharp.Formats
{ {
result[i].Init(); result[i].Init();
} }
return result; return result;
} }
@ -172,18 +164,25 @@ namespace ImageSharp.Formats
public float this[int index] public float this[int index]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return this.Data[index]; } get
{
return this.Data[index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
set { this.Data[index] = value; } set
{
this.Data[index] = value;
}
} }
// TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement! // TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement!
public void Dispose() public void Dispose()
{ {
if (Data != null) if (this.Data != null)
{ {
ArrayPool.Return(Data, true); ArrayPool.Return(this.Data, true);
Data = null; this.Data = null;
} }
} }
@ -195,22 +194,19 @@ namespace ImageSharp.Formats
} }
} }
public void Clear() public void Clear()
{ {
for (int i = 0; i < Data.Length; i++) for (int i = 0; i < this.Data.Length; i++)
{ {
Data[i] = 0; this.Data[i] = 0;
} }
} }
public BlockF Clone() public BlockF Clone()
{ {
BlockF clone = Create(); BlockF clone = Create();
Array.Copy(Data, clone.Data, BlockSize); Array.Copy(this.Data, clone.Data, BlockSize);
return clone; return clone;
} }
} }
}
}

35
src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.cs

@ -1,4 +1,5 @@
 // <auto-generated />
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -8,6 +9,14 @@ namespace ImageSharp.Formats
{ {
internal partial struct Block8x8F internal partial struct Block8x8F
{ {
private static readonly Vector4 CMin4 = new Vector4(-128f);
private static readonly Vector4 CMax4 = new Vector4(127f);
private static readonly Vector4 COff4 = new Vector4(128f);
/// <summary>
/// Transpose the block into d
/// </summary>
/// <param name="d">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TransposeInto(ref Block8x8F d) public void TransposeInto(ref Block8x8F d)
{ {
@ -21,24 +30,12 @@ namespace ImageSharp.Formats
d.V0R.W = V7L.X; d.V1R.W = V7L.Y; d.V2R.W = V7L.Z; d.V3R.W = V7L.W; d.V4R.W = V7R.X; d.V5R.W = V7R.Y; d.V6R.W = V7R.Z; d.V7R.W = V7R.W; d.V0R.W = V7L.X; d.V1R.W = V7L.Y; d.V2R.W = V7L.Z; d.V3R.W = V7L.W; d.V4R.W = V7R.X; d.V5R.W = V7R.Y; d.V6R.W = V7R.Z; d.V7R.W = V7R.W;
} }
/// <summary>
public void CropInto(float min, float max, ref Block8x8F d) /// Level shift by +128, clip to [0, 255]
{ /// </summary>
Vector4 minVec = new Vector4(min); /// <param name="d">Destination</param>
Vector4 maxVec = new Vector4(max); [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d)
d.V0L = Vector4.Max(Vector4.Min(V0L, maxVec), minVec);d.V0R = Vector4.Max(Vector4.Min(V0R, maxVec), minVec);
d.V1L = Vector4.Max(Vector4.Min(V1L, maxVec), minVec);d.V1R = Vector4.Max(Vector4.Min(V1R, maxVec), minVec);
d.V2L = Vector4.Max(Vector4.Min(V2L, maxVec), minVec);d.V2R = Vector4.Max(Vector4.Min(V2R, maxVec), minVec);
d.V3L = Vector4.Max(Vector4.Min(V3L, maxVec), minVec);d.V3R = Vector4.Max(Vector4.Min(V3R, maxVec), minVec);
d.V4L = Vector4.Max(Vector4.Min(V4L, maxVec), minVec);d.V4R = Vector4.Max(Vector4.Min(V4R, maxVec), minVec);
d.V5L = Vector4.Max(Vector4.Min(V5L, maxVec), minVec);d.V5R = Vector4.Max(Vector4.Min(V5R, maxVec), minVec);
d.V6L = Vector4.Max(Vector4.Min(V6L, maxVec), minVec);d.V6R = Vector4.Max(Vector4.Min(V6R, maxVec), minVec);
d.V7L = Vector4.Max(Vector4.Min(V7L, maxVec), minVec);d.V7R = Vector4.Max(Vector4.Min(V7R, maxVec), minVec);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ColorifyInto(ref Block8x8F d)
{ {
d.V0L = Vector4.Max(Vector4.Min(V0L, CMax4), CMin4) + COff4;d.V0R = Vector4.Max(Vector4.Min(V0R, CMax4), CMin4) + COff4; d.V0L = Vector4.Max(Vector4.Min(V0L, CMax4), CMin4) + COff4;d.V0R = Vector4.Max(Vector4.Min(V0R, CMax4), CMin4) + COff4;
d.V1L = Vector4.Max(Vector4.Min(V1L, CMax4), CMin4) + COff4;d.V1R = Vector4.Max(Vector4.Min(V1R, CMax4), CMin4) + COff4; d.V1L = Vector4.Max(Vector4.Min(V1L, CMax4), CMin4) + COff4;d.V1R = Vector4.Max(Vector4.Min(V1R, CMax4), CMin4) + COff4;

45
src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.tt

@ -4,19 +4,28 @@
<#@ import namespace="System.Text" #> <#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #> <#@ output extension=".cs" #>
// <auto-generated />
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
<# <#
char[] coordz = new[] {'X', 'Y', 'Z', 'W'}; char[] coordz = {'X', 'Y', 'Z', 'W'};
#> #>
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
internal partial struct Block8x8F internal partial struct Block8x8F
{ {
private static readonly Vector4 CMin4 = new Vector4(-128f);
private static readonly Vector4 CMax4 = new Vector4(127f);
private static readonly Vector4 COff4 = new Vector4(128f);
/// <summary>
/// Transpose the block into d
/// </summary>
/// <param name="d">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TransposeInto(ref Block8x8F d) public void TransposeInto(ref Block8x8F d)
{ {
@ -34,33 +43,7 @@ namespace ImageSharp.Formats
char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R'; char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R';
string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord}; "; string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord}; ";
//bld.Append(expression);
Write(expression); Write(expression);
}
//bld.AppendLine();
WriteLine("");
}
PopIndent();
//Write(bld.ToString());
#>
}
public void CropInto(float min, float max, ref Block8x8F d)
{
Vector4 minVec = new Vector4(min);
Vector4 maxVec = new Vector4(max);
<#
PushIndent(" ");
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 2; j++)
{
char side = j == 0 ? 'L' : 'R';
Write($"d.V{i}{side} = Vector4.Max(Vector4.Min(V{i}{side}, maxVec), minVec);");
} }
WriteLine(""); WriteLine("");
} }
@ -68,8 +51,12 @@ namespace ImageSharp.Formats
#> #>
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// <summary>
internal void ColorifyInto(ref Block8x8F d) /// Level shift by +128, clip to [0, 255]
/// </summary>
/// <param name="d">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d)
{ {
<# <#

494
src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs

@ -1,13 +1,15 @@
using System; // <copyright file="Block8x8F.cs" company="James Jackson-South">
using System.Buffers; // Copyright (c) James Jackson-South and contributors.
using System.Numerics; // Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices; // </copyright>
using System.Runtime.InteropServices;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary> /// <summary>
/// DCT code Ported from https://github.com/norishigefukushima/dct_simd /// DCT code Ported from https://github.com/norishigefukushima/dct_simd
/// </summary> /// </summary>
@ -37,251 +39,193 @@ namespace ImageSharp.Formats
public Vector4 V7L; public Vector4 V7L;
public Vector4 V7R; public Vector4 V7R;
public const int VectorCount = 16; public const int VectorCount = 16;
public const int ScalarCount = VectorCount*4; public const int ScalarCount = VectorCount*4;
private static readonly ArrayPool<float> ScalarArrayPool = ArrayPool<float>.Create(ScalarCount, 50); /// <summary>
/// Load raw 32bit floating point data from source
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void LoadFrom(MutableSpan<float> source) public unsafe void LoadFrom(MutableSpan<float> source)
{ {
fixed (Vector4* ptr = &V0L) fixed (void* ptr = &this.V0L)
{ {
Marshal.Copy(source.Data, source.Offset, (IntPtr) ptr, ScalarCount); Marshal.Copy(source.Data, source.Offset, (IntPtr)ptr, ScalarCount);
//float* fp = (float*)ptr;
//for (int i = 0; i < ScalarCount; i++)
//{
// fp[i] = source[i];
//}
} }
} }
/// <summary>
/// Load raw 32bit floating point data from source
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void CopyTo(MutableSpan<float> dest) public static unsafe void LoadFrom(Block8x8F* blockPtr, MutableSpan<float> source)
{ {
fixed (Vector4* ptr = &V0L) Marshal.Copy(source.Data, source.Offset, (IntPtr)blockPtr, ScalarCount);
}
/// <summary>
/// Load raw 32bit floating point data from source
/// </summary>
internal unsafe void LoadFrom(MutableSpan<int> source)
{
fixed (Vector4* ptr = &this.V0L)
{ {
Marshal.Copy((IntPtr) ptr, dest.Data, dest.Offset, ScalarCount); float* fp = (float*)ptr;
for (int i = 0; i < ScalarCount; i++)
{
fp[i] = source[i];
}
} }
} }
/// <summary>
/// Copy raw 32bit floating point data to dest
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void CopyTo(float[] dest) public unsafe void CopyTo(MutableSpan<float> dest)
{ {
fixed (Vector4* ptr = &V0L) fixed (void* ptr = &this.V0L)
{ {
Marshal.Copy((IntPtr) ptr, dest, 0, ScalarCount); Marshal.Copy((IntPtr)ptr, dest.Data, dest.Offset, ScalarCount);
} }
} }
/// <summary>
/// Copy raw 32bit floating point data to dest
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void LoadFrom(Block8x8F* blockPtr, MutableSpan<float> source) public unsafe void CopyTo(float[] dest)
{ {
Marshal.Copy(source.Data, source.Offset, (IntPtr) blockPtr, ScalarCount); fixed (void* ptr = &this.V0L)
{
Marshal.Copy((IntPtr)ptr, dest, 0, ScalarCount);
}
} }
/// <summary>
/// Copy raw 32bit floating point data to dest
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan<float> dest) public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan<float> dest)
{ {
Marshal.Copy((IntPtr) blockPtr, dest.Data, dest.Offset, ScalarCount); Marshal.Copy((IntPtr)blockPtr, dest.Data, dest.Offset, ScalarCount);
}
internal unsafe void LoadFrom(MutableSpan<int> source)
{
fixed (Vector4* ptr = &V0L)
{
float* fp = (float*) ptr;
for (int i = 0; i < ScalarCount; i++)
{
fp[i] = source[i];
}
}
} }
/// <summary>
/// Copy raw 32bit floating point data to dest
/// </summary>
internal unsafe void CopyTo(MutableSpan<int> dest) internal unsafe void CopyTo(MutableSpan<int> dest)
{ {
fixed (Vector4* ptr = &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 < ScalarCount; i++)
{ {
dest[i] = (int) fp[i]; dest[i] = (int)fp[i];
} }
} }
} }
public unsafe void TransposeInplace()
{
fixed (Vector4* ptr = &V0L)
{
float* data = (float*) ptr;
for (int i = 1; i < 8; i++)
{
int i8 = i*8;
for (int j = 0; j < i; j++)
{
float tmp = data[i8 + j];
data[i8 + j] = data[j*8 + i];
data[j*8 + i] = tmp;
}
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MultiplyAllInplace(Vector4 s) public void MultiplyAllInplace(Vector4 s)
{ {
V0L *= s; this.V0L *= s;
V0R *= s; this.V0R *= s;
V1L *= s; this.V1L *= s;
V1R *= s; this.V1R *= s;
V2L *= s; this.V2L *= s;
V2R *= s; this.V2R *= s;
V3L *= s; this.V3L *= s;
V3R *= s; this.V3R *= s;
V4L *= s; this.V4L *= s;
V4R *= s; this.V4R *= s;
V5L *= s; this.V5L *= s;
V5R *= s; this.V5R *= s;
V6L *= s; this.V6L *= s;
V6R *= s; this.V6R *= s;
V7L *= s; this.V7L *= s;
V7R *= s; this.V7R *= s;
} }
// ReSharper disable once InconsistentNaming /// <summary>
public void IDCTInto(ref Block8x8F dest, ref Block8x8F temp) /// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization)
/// </summary>
/// <param name="dest">Destination</param>
/// <param name="temp">Temporary block provided by the caller</param>
public void TransformIDCTInto(ref Block8x8F dest, ref Block8x8F temp)
{ {
TransposeInto(ref temp); this.TransposeInto(ref temp);
temp.iDCT2D8x4_LeftPart(ref dest); temp.IDCT8x4_LeftPart(ref dest);
temp.iDCT2D8x4_RightPart(ref dest); temp.IDCT8x4_RightPart(ref dest);
dest.TransposeInto(ref temp); dest.TransposeInto(ref temp);
temp.iDCT2D8x4_LeftPart(ref dest); temp.IDCT8x4_LeftPart(ref dest);
temp.iDCT2D8x4_RightPart(ref dest); temp.IDCT8x4_RightPart(ref dest);
dest.MultiplyAllInplace(_0_125);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] dest.MultiplyAllInplace(c_0_125);
public void IDCTInplace()
{
Block8x8F result = new Block8x8F();
Block8x8F temp = new Block8x8F();
IDCTInto(ref result, ref temp);
this = result;
} }
private static readonly Vector4 _1_175876 = new Vector4(1.175876f); private static readonly Vector4 c_1_175876 = new Vector4(1.175876f);
private static readonly Vector4 _1_961571 = new Vector4(-1.961571f); private static readonly Vector4 c_1_961571 = new Vector4(-1.961571f);
private static readonly Vector4 _0_390181 = new Vector4(-0.390181f); private static readonly Vector4 c_0_390181 = new Vector4(-0.390181f);
private static readonly Vector4 _0_899976 = new Vector4(-0.899976f); private static readonly Vector4 c_0_899976 = new Vector4(-0.899976f);
private static readonly Vector4 _2_562915 = new Vector4(-2.562915f); private static readonly Vector4 c_2_562915 = new Vector4(-2.562915f);
private static readonly Vector4 _0_298631 = new Vector4(0.298631f); private static readonly Vector4 c_0_298631 = new Vector4(0.298631f);
private static readonly Vector4 _2_053120 = new Vector4(2.053120f); private static readonly Vector4 c_2_053120 = new Vector4(2.053120f);
private static readonly Vector4 _3_072711 = new Vector4(3.072711f); private static readonly Vector4 c_3_072711 = new Vector4(3.072711f);
private static readonly Vector4 _1_501321 = new Vector4(1.501321f); private static readonly Vector4 c_1_501321 = new Vector4(1.501321f);
private static readonly Vector4 _0_541196 = new Vector4(0.541196f); private static readonly Vector4 c_0_541196 = new Vector4(0.541196f);
private static readonly Vector4 _1_847759 = new Vector4(-1.847759f); private static readonly Vector4 c_1_847759 = new Vector4(-1.847759f);
private static readonly Vector4 _0_765367 = new Vector4(0.765367f); private static readonly Vector4 c_0_765367 = new Vector4(0.765367f);
private static readonly Vector4 _0_125 = new Vector4(0.1250f);
private static readonly Vector4 c_0_125 = new Vector4(0.1250f);
/// <summary>
/// Do IDCT internal operations on the left part of the block. Original source:
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
/// </summary>
/// <param name="d">Destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void iDCT2D8x4_LeftPart(ref Block8x8F d) internal void IDCT8x4_LeftPart(ref Block8x8F d)
{ {
/* Vector4 my1 = this.V1L;
float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; Vector4 my7 = this.V7L;
for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); }
*/
/*
0: 1.414214
1: 1.387040
2: 1.306563
3:
4: 1.000000
5: 0.785695
6:
7: 0.275899
*/
Vector4 my1 = V1L;
Vector4 my7 = V7L;
Vector4 mz0 = my1 + my7; Vector4 mz0 = my1 + my7;
Vector4 my3 = V3L; Vector4 my3 = this.V3L;
Vector4 mz2 = my3 + my7; Vector4 mz2 = my3 + my7;
Vector4 my5 = V5L; Vector4 my5 = this.V5L;
Vector4 mz1 = my3 + my5; Vector4 mz1 = my3 + my5;
Vector4 mz3 = my1 + my5; Vector4 mz3 = my1 + my5;
Vector4 mz4 = ((mz0 + mz1)*_1_175876); Vector4 mz4 = ((mz0 + mz1) * c_1_175876);
//z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5];
//z4 = (z0 + z1) * r[3]; mz2 = (mz2 * c_1_961571) + mz4;
mz3 = (mz3 * c_0_390181) + mz4;
mz2 = mz2*_1_961571 + mz4; mz0 = mz0 * c_0_899976;
mz3 = mz3*_0_390181 + mz4; mz1 = mz1 * c_2_562915;
mz0 = mz0*_0_899976;
mz1 = mz1*_2_562915; Vector4 mb3 = (my7 * c_0_298631) + mz0 + mz2;
Vector4 mb2 = (my5 * c_2_053120) + mz1 + mz3;
/* Vector4 mb1 = (my3 * c_3_072711) + mz1 + mz2;
-0.899976 Vector4 mb0 = (my1 * c_1_501321) + mz0 + mz3;
-2.562915
-1.961571 Vector4 my2 = this.V2L;
-0.390181 Vector4 my6 = this.V6L;
z0 = z0 * (-r[3] + r[7]); mz4 = (my2 + my6) * c_0_541196;
z1 = z1 * (-r[3] - r[1]); Vector4 my0 = this.V0L;
z2 = z2 * (-r[3] - r[5]) + z4; Vector4 my4 = this.V4L;
z3 = z3 * (-r[3] + r[5]) + z4;*/
Vector4 mb3 = my7*_0_298631 + mz0 + mz2;
Vector4 mb2 = my5*_2_053120 + mz1 + mz3;
Vector4 mb1 = my3*_3_072711 + mz1 + mz2;
Vector4 mb0 = my1*_1_501321 + mz0 + mz3;
/*
0.298631
2.053120
3.072711
1.501321
b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2;
b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3;
b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2;
b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3;
*/
Vector4 my2 = V2L;
Vector4 my6 = V6L;
mz4 = (my2 + my6)*_0_541196;
Vector4 my0 = V0L;
Vector4 my4 = V4L;
mz0 = my0 + my4; mz0 = my0 + my4;
mz1 = my0 - my4; mz1 = my0 - my4;
mz2 = mz4 + my6*_1_847759; mz2 = mz4 + (my6 * c_1_847759);
mz3 = mz4 + my2*_0_765367; mz3 = mz4 + (my2 * c_0_765367);
my0 = mz0 + mz3; my0 = mz0 + mz3;
my3 = mz0 - mz3; my3 = mz0 - mz3;
my1 = mz1 + mz2; my1 = mz1 + mz2;
my2 = mz1 - mz2; my2 = mz1 - mz2;
/*
1.847759
0.765367
z4 = (y[2] + y[6]) * r[6];
z0 = y[0] + y[4]; z1 = y[0] - y[4];
z2 = z4 - y[6] * (r[2] + r[6]);
z3 = z4 + y[2] * (r[2] - r[6]);
a0 = z0 + z3; a3 = z0 - z3;
a1 = z1 + z2; a2 = z1 - z2;
*/
d.V0L = my0 + mb0; d.V0L = my0 + mb0;
d.V7L = my0 - mb0; d.V7L = my0 - mb0;
@ -291,104 +235,53 @@ namespace ImageSharp.Formats
d.V5L = my2 - mb2; d.V5L = my2 - mb2;
d.V3L = my3 + mb3; d.V3L = my3 + mb3;
d.V4L = my3 - mb3; d.V4L = my3 - mb3;
/*
x[0] = a0 + b0; x[7] = a0 - b0;
x[1] = a1 + b1; x[6] = a1 - b1;
x[2] = a2 + b2; x[5] = a2 - b2;
x[3] = a3 + b3; x[4] = a3 - b3;
for(i = 0;i < 8;i++){ x[i] *= 0.353554f; }
*/
} }
/// <summary>
/// Do IDCT internal operations on the right part of the block.
/// Original source:
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void iDCT2D8x4_RightPart(ref Block8x8F d) internal void IDCT8x4_RightPart(ref Block8x8F d)
{ {
/* Vector4 my1 = this.V1R;
float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; Vector4 my7 = this.V7R;
for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); }
*/
/*
0: 1.414214
1: 1.387040
2: 1.306563
3:
4: 1.000000
5: 0.785695
6:
7: 0.275899
*/
Vector4 my1 = V1R;
Vector4 my7 = V7R;
Vector4 mz0 = my1 + my7; Vector4 mz0 = my1 + my7;
Vector4 my3 = V3R; Vector4 my3 = this.V3R;
Vector4 mz2 = my3 + my7; Vector4 mz2 = my3 + my7;
Vector4 my5 = V5R; Vector4 my5 = this.V5R;
Vector4 mz1 = my3 + my5; Vector4 mz1 = my3 + my5;
Vector4 mz3 = my1 + my5; Vector4 mz3 = my1 + my5;
Vector4 mz4 = ((mz0 + mz1)*_1_175876); Vector4 mz4 = (mz0 + mz1) * c_1_175876;
//z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5];
//z4 = (z0 + z1) * r[3]; mz2 = (mz2 * c_1_961571) + mz4;
mz3 = (mz3 * c_0_390181) + mz4;
mz2 = mz2*_1_961571 + mz4; mz0 = mz0 * c_0_899976;
mz3 = mz3*_0_390181 + mz4; mz1 = mz1 * c_2_562915;
mz0 = mz0*_0_899976;
mz1 = mz1*_2_562915; Vector4 mb3 = (my7 * c_0_298631) + mz0 + mz2;
Vector4 mb2 = (my5 * c_2_053120) + mz1 + mz3;
/* Vector4 mb1 = (my3 * c_3_072711) + mz1 + mz2;
-0.899976 Vector4 mb0 = (my1 * c_1_501321) + mz0 + mz3;
-2.562915
-1.961571 Vector4 my2 = this.V2R;
-0.390181 Vector4 my6 = this.V6R;
z0 = z0 * (-r[3] + r[7]); mz4 = (my2 + my6) * c_0_541196;
z1 = z1 * (-r[3] - r[1]); Vector4 my0 = this.V0R;
z2 = z2 * (-r[3] - r[5]) + z4; Vector4 my4 = this.V4R;
z3 = z3 * (-r[3] + r[5]) + z4;*/
Vector4 mb3 = my7*_0_298631 + mz0 + mz2;
Vector4 mb2 = my5*_2_053120 + mz1 + mz3;
Vector4 mb1 = my3*_3_072711 + mz1 + mz2;
Vector4 mb0 = my1*_1_501321 + mz0 + mz3;
/*
0.298631
2.053120
3.072711
1.501321
b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2;
b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3;
b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2;
b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3;
*/
Vector4 my2 = V2R;
Vector4 my6 = V6R;
mz4 = (my2 + my6)*_0_541196;
Vector4 my0 = V0R;
Vector4 my4 = V4R;
mz0 = my0 + my4; mz0 = my0 + my4;
mz1 = my0 - my4; mz1 = my0 - my4;
mz2 = mz4 + my6*_1_847759; mz2 = mz4 + (my6 * c_1_847759);
mz3 = mz4 + my2*_0_765367; mz3 = mz4 + (my2 * c_0_765367);
my0 = mz0 + mz3; my0 = mz0 + mz3;
my3 = mz0 - mz3; my3 = mz0 - mz3;
my1 = mz1 + mz2; my1 = mz1 + mz2;
my2 = mz1 - mz2; my2 = mz1 - mz2;
/*
1.847759
0.765367
z4 = (y[2] + y[6]) * r[6];
z0 = y[0] + y[4]; z1 = y[0] - y[4];
z2 = z4 - y[6] * (r[2] + r[6]);
z3 = z4 + y[2] * (r[2] - r[6]);
a0 = z0 + z3; a3 = z0 - z3;
a1 = z1 + z2; a2 = z1 - z2;
*/
d.V0R = my0 + mb0; d.V0R = my0 + mb0;
d.V7R = my0 - mb0; d.V7R = my0 - mb0;
@ -398,15 +291,7 @@ namespace ImageSharp.Formats
d.V5R = my2 - mb2; d.V5R = my2 - mb2;
d.V3R = my3 + mb3; d.V3R = my3 + mb3;
d.V4R = my3 - mb3; d.V4R = my3 - mb3;
/*
x[0] = a0 + b0; x[7] = a0 - b0;
x[1] = a1 + b1; x[6] = a1 - b1;
x[2] = a2 + b2; x[5] = a2 - b2;
x[3] = a3 + b3; x[4] = a3 - b3;
for(i = 0;i < 8;i++){ x[i] *= 0.353554f; }
*/
} }
public unsafe float this[int idx] public unsafe float this[int idx]
{ {
@ -415,83 +300,94 @@ namespace ImageSharp.Formats
{ {
fixed (Block8x8F* p = &this) fixed (Block8x8F* p = &this)
{ {
float* fp = (float*) p; float* fp = (float*)p;
return fp[idx]; return fp[idx];
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
set set
{ {
fixed (Block8x8F* p = &this) fixed (Block8x8F* p = &this)
{ {
float* fp = (float*) p; float* fp = (float*)p;
fp[idx] = value; fp[idx] = value;
} }
} }
} }
/// <summary>
/// Pointer-based "Indexer" (getter part)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx) internal static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx)
{ {
float* fp = (float*) blockPtr; float* fp = (float*)blockPtr;
return fp[idx]; return fp[idx];
} }
/// <summary>
/// Pointer-based "Indexer" (setter part)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value) internal static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value)
{ {
float* fp = (float*) blockPtr; float* fp = (float*)blockPtr;
fp[idx] = value; fp[idx] = value;
} }
/// <summary>
/// Fill the block with defaults (zeroes)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Clear() internal void Clear()
{ {
this = new Block8x8F(); // LOL C# Plz! // The cheapest way to do this in C#:
this = new Block8x8F();
} }
/// <summary>
/// TODO: Should be removed when BlockF goes away
/// </summary>
/// <param name="legacyBlock"></param>
internal void LoadFrom(ref BlockF legacyBlock) internal void LoadFrom(ref BlockF legacyBlock)
{ {
LoadFrom(legacyBlock.Data); this.LoadFrom(legacyBlock.Data);
} }
/// <summary>
/// TODO: Should be removed when BlockF goes away
/// </summary>
/// <param name="legacyBlock"></param>
internal void CopyTo(ref BlockF legacyBlock) internal void CopyTo(ref BlockF legacyBlock)
{ {
CopyTo(legacyBlock.Data); this.CopyTo(legacyBlock.Data);
} }
private static readonly Vector4 CMin4 = new Vector4(-128f);
private static readonly Vector4 CMax4 = new Vector4(127f);
private static readonly Vector4 COff4 = new Vector4(128f);
/// <summary> /// <summary>
/// Level shift by +128, clip to [0, 255], and write to buffer. /// Level shift by +128, clip to [0, 255], and write to buffer.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe void CopyColorsTo( internal unsafe void CopyColorsTo(MutableSpan<byte> buffer, int stride, Block8x8F* temp)
MutableSpan<byte> buffer,
int stride,
Block8x8F* temp)
{ {
ColorifyInto(ref *temp); this.TransformByteConvetibleColorValuesInto(ref *temp);
float* src = (float*) temp; float* src = (float*)temp;
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
buffer[0] = (byte) src[0]; buffer[0] = (byte)src[0];
buffer[1] = (byte) src[1]; buffer[1] = (byte)src[1];
buffer[2] = (byte) src[2]; buffer[2] = (byte)src[2];
buffer[3] = (byte) src[3]; buffer[3] = (byte)src[3];
buffer[4] = (byte) src[4]; buffer[4] = (byte)src[4];
buffer[5] = (byte) src[5]; buffer[5] = (byte)src[5];
buffer[6] = (byte) src[6]; buffer[6] = (byte)src[6];
buffer[7] = (byte) src[7]; buffer[7] = (byte)src[7];
buffer.AddOffset(stride); buffer.AddOffset(stride);
src += 8; src += 8;
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void UnZig(Block8x8F* block, Block8x8F* qt, int* unzigPtr) internal static unsafe void UnZig(Block8x8F* block, Block8x8F* qt, int* unzigPtr)
{ {

44
src/ImageSharp/Formats/Jpg/Components/Bytes.cs

@ -2,14 +2,13 @@
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
/// <summary> /// <summary>
/// Bytes is a byte buffer, similar to a stream, except that it /// Bytes is a byte buffer, similar to a stream, except that it
/// has to be able to unread more than 1 byte, due to byte stuffing. /// has to be able to unread more than 1 byte, due to byte stuffing.
@ -17,25 +16,14 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
internal struct Bytes : IDisposable internal struct Bytes : IDisposable
{ {
/// <summary>
/// Initializes a new instance of the <see cref="Bytes"/> class.
/// </summary>
//public Bytes()
//{
// this.Buffer = new byte[4096];
// this.I = 0;
// this.J = 0;
// this.UnreadableBytes = 0;
//}
private static readonly ArrayPool<byte> ArrayPool = ArrayPool<byte>.Create(4096, 50); private static readonly ArrayPool<byte> ArrayPool = ArrayPool<byte>.Create(4096, 50);
/// <summary>
/// Creates a new instance of the <see cref="Bytes"/>, and initializes it's buffer.
/// </summary>
public static Bytes Create() public static Bytes Create()
{ {
return new Bytes return new Bytes { Buffer = ArrayPool.Rent(4096) };
{
Buffer = ArrayPool.Rent(4096)
};
} }
/// <summary> /// <summary>
@ -57,15 +45,14 @@ namespace ImageSharp.Formats
public void Dispose() public void Dispose()
{ {
if (Buffer!= null) ArrayPool.Return(Buffer); if (this.Buffer != null) ArrayPool.Return(this.Buffer);
Buffer = null; this.Buffer = null;
} }
/// <summary> /// <summary>
/// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data. /// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data.
/// </summary> /// </summary>
/// <returns>The <see cref="byte"/></returns> /// <returns>The <see cref="byte"/></returns>
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByteStuffedByte(Stream inputStream, out JpegDecoderCore.ErrorCodes errorCode) internal byte ReadByteStuffedByte(Stream inputStream, out JpegDecoderCore.ErrorCodes errorCode)
{ {
byte x; byte x;
@ -87,7 +74,8 @@ namespace ImageSharp.Formats
{ {
errorCode = JpegDecoderCore.ErrorCodes.MissingFF00; errorCode = JpegDecoderCore.ErrorCodes.MissingFF00;
return 0; return 0;
//throw new MissingFF00Exception();
// throw new MissingFF00Exception();
} }
this.I++; this.I++;
@ -110,7 +98,8 @@ namespace ImageSharp.Formats
{ {
errorCode = JpegDecoderCore.ErrorCodes.MissingFF00; errorCode = JpegDecoderCore.ErrorCodes.MissingFF00;
return 0; return 0;
//throw new MissingFF00Exception();
// throw new MissingFF00Exception();
} }
return JpegConstants.Markers.XFF; return JpegConstants.Markers.XFF;
@ -165,6 +154,5 @@ namespace ImageSharp.Formats
this.J += n; this.J += n;
} }
} }
} }

42
src/ImageSharp/Formats/Jpg/Components/Huffman.cs

@ -2,42 +2,30 @@
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System;
using System.Buffers;
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System;
using System.Buffers;
/// <summary> /// <summary>
/// Represents a Huffman tree /// Represents a Huffman tree
/// </summary> /// </summary>
internal struct Huffman : IDisposable internal struct Huffman : IDisposable
{ {
private static ArrayPool<ushort> UshortBuffer = private static ArrayPool<ushort> UshortBuffer = ArrayPool<ushort>.Create(1 << JpegDecoderCore.LutSize, 50);
//ArrayPool<ushort>.Shared;
ArrayPool<ushort>.Create(1 << JpegDecoderCore.LutSize, 50);
private static ArrayPool<byte> ByteBuffer = private static ArrayPool<byte> ByteBuffer = ArrayPool<byte>.Create(JpegDecoderCore.MaxNCodes, 50);
//ArrayPool<byte>.Shared;
ArrayPool<byte>.Create(JpegDecoderCore.MaxNCodes, 50);
private static readonly ArrayPool<int> IntBuffer = private static readonly ArrayPool<int> IntBuffer = ArrayPool<int>.Create(JpegDecoderCore.MaxCodeLength, 50);
//ArrayPool<int>.Shared;
ArrayPool<int>.Create(JpegDecoderCore.MaxCodeLength, 50);
public void Init(int lutSize, int maxNCodes, int maxCodeLength) public void Init(int lutSize, int maxNCodes, int maxCodeLength)
{ {
//this.Lut = new ushort[1 << lutSize];
//this.Values = new byte[maxNCodes];
//this.MinCodes = new int[maxCodeLength];
//this.MaxCodes = new int[maxCodeLength];
//this.Indices = new int[maxCodeLength];
this.Lut = UshortBuffer.Rent(1 << lutSize); this.Lut = UshortBuffer.Rent(1 << lutSize);
this.Values = ByteBuffer.Rent(maxNCodes); this.Values = ByteBuffer.Rent(maxNCodes);
this.MinCodes = IntBuffer.Rent(maxCodeLength); this.MinCodes = IntBuffer.Rent(maxCodeLength);
this.MaxCodes = IntBuffer.Rent(maxCodeLength); ; this.MaxCodes = IntBuffer.Rent(maxCodeLength);
this.Indices = IntBuffer.Rent(maxCodeLength); ; this.Indices = IntBuffer.Rent(maxCodeLength);
} }
/// <summary> /// <summary>
@ -77,13 +65,11 @@ namespace ImageSharp.Formats
public void Dispose() public void Dispose()
{ {
UshortBuffer.Return(Lut, true); UshortBuffer.Return(this.Lut, true);
ByteBuffer.Return(Values, true); ByteBuffer.Return(this.Values, true);
IntBuffer.Return(MinCodes, true); IntBuffer.Return(this.MinCodes, true);
IntBuffer.Return(MaxCodes, true); IntBuffer.Return(this.MaxCodes, true);
IntBuffer.Return(Indices, true); IntBuffer.Return(this.Indices, true);
} }
} }
} }

2
src/ImageSharp/Formats/Jpg/Components/IDCT.cs

@ -3,8 +3,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System.Numerics;
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
/// <summary> /// <summary>

45
src/ImageSharp/Formats/Jpg/Components/MutableSpan.cs

@ -1,52 +1,67 @@
using System.Buffers; // <copyright file="MutableSpan.cs" company="James Jackson-South">
using System.Numerics; // Copyright (c) James Jackson-South and contributors.
using System.Runtime.CompilerServices; // Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary> /// <summary>
/// Like corefxlab Span, but with an AddOffset() method for efficiency. /// Like corefxlab Span, but with an AddOffset() method for efficiency.
/// TODO: When Span will be official, consider replacing this class! /// TODO: When Span will be official, consider replacing this class!
/// </summary> /// </summary>
/// <see cref="https://github.com/dotnet/corefxlab/blob/master/src/System.Slices/System/Span.cs"/> /// <see>
/// <cref>https://github.com/dotnet/corefxlab/blob/master/src/System.Slices/System/Span.cs</cref>
/// </see>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal struct MutableSpan<T> internal struct MutableSpan<T>
{ {
public T[] Data; public T[] Data;
public int Offset; public int Offset;
public int TotalCount => Data.Length - Offset; public int TotalCount => this.Data.Length - this.Offset;
public MutableSpan(int size, int offset = 0) public MutableSpan(int size, int offset = 0)
{ {
Data = new T[size]; this.Data = new T[size];
Offset = offset; this.Offset = offset;
} }
public MutableSpan(T[] data, int offset = 0) public MutableSpan(T[] data, int offset = 0)
{ {
Data = data; this.Data = data;
Offset = offset; this.Offset = offset;
} }
public T this[int idx] public T this[int idx]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Data[idx + Offset]; } [MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)] set { Data[idx + Offset] = value; } get
{
return this.Data[idx + this.Offset];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.Data[idx + this.Offset] = value;
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public MutableSpan<T> Slice(int offset) public MutableSpan<T> Slice(int offset)
{ {
return new MutableSpan<T>(Data, Offset + offset); return new MutableSpan<T>(this.Data, this.Offset + offset);
} }
public static implicit operator MutableSpan<T>(T[] data) => new MutableSpan<T>(data, 0); public static implicit operator MutableSpan<T>(T[] data) => new MutableSpan<T>(data, 0);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddOffset(int offset) public void AddOffset(int offset)
{ {
Offset += offset; this.Offset += offset;
} }
} }
@ -89,7 +104,5 @@ namespace ImageSharp.Formats
data[2] = (int)v.Z; data[2] = (int)v.Z;
data[3] = (int)v.W; data[3] = (int)v.W;
} }
} }
} }

447
src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs

@ -3,14 +3,12 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System; using System;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
@ -71,12 +69,12 @@ namespace ImageSharp.Formats
/// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
/// </summary> /// </summary>
private static readonly int[] Unzig = private static readonly int[] Unzig =
{ {
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33,
33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50,
50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46,
39, 46, 53, 60, 61, 54, 47, 55, 62, 63, 53, 60, 61, 54, 47, 55, 62, 63,
}; };
/// <summary> /// <summary>
/// The component array /// The component array
@ -92,7 +90,6 @@ namespace ImageSharp.Formats
/// The huffman trees /// The huffman trees
/// </summary> /// </summary>
//private readonly Huffman[,] huffmanTrees; //private readonly Huffman[,] huffmanTrees;
private readonly Huffman[] huffmanTrees; private readonly Huffman[] huffmanTrees;
/// <summary> /// <summary>
@ -223,7 +220,6 @@ namespace ImageSharp.Formats
} }
} }
/// <summary> /// <summary>
/// Decodes the image from the specified this._stream and sets /// Decodes the image from the specified this._stream and sets
/// the data to image. /// the data to image.
@ -234,8 +230,7 @@ namespace ImageSharp.Formats
/// <param name="stream">The stream, where the image should be.</param> /// <param name="stream">The stream, where the image should be.</param>
/// <param name="configOnly">Whether to decode metadata only.</param> /// <param name="configOnly">Whether to decode metadata only.</param>
public void Decode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream, bool configOnly) public void Decode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream, bool configOnly)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
where TPacked : struct
{ {
this.inputStream = stream; this.inputStream = stream;
@ -373,7 +368,8 @@ namespace ImageSharp.Formats
this.ProcessApp14Marker(remaining); this.ProcessApp14Marker(remaining);
break; break;
default: default:
if ((JpegConstants.Markers.APP0 <= marker && marker <= JpegConstants.Markers.APP15) || marker == JpegConstants.Markers.COM) if ((JpegConstants.Markers.APP0 <= marker && marker <= JpegConstants.Markers.APP15)
|| marker == JpegConstants.Markers.COM)
{ {
this.Skip(remaining); this.Skip(remaining);
} }
@ -422,7 +418,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Missing SOS marker."); throw new ImageFormatException("Missing SOS marker.");
} }
} }
/// <summary> /// <summary>
/// Processes a Define Huffman Table marker, and initializes a huffman /// Processes a Define Huffman Table marker, and initializes a huffman
/// struct from its contents. Specified in section B.2.4.2. /// struct from its contents. Specified in section B.2.4.2.
@ -451,13 +447,12 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Bad Th value"); throw new ImageFormatException("Bad Th value");
} }
ProcessDefineHuffmanTablesMarkerLoop(ref this.huffmanTrees[tc * ThRowSize + th], ref remaining); this.ProcessDefineHuffmanTablesMarkerLoop(ref this.huffmanTrees[tc * ThRowSize + th], ref remaining);
} }
} }
private void ProcessDefineHuffmanTablesMarkerLoop(ref Huffman huffman, ref int remaining) private void ProcessDefineHuffmanTablesMarkerLoop(ref Huffman huffman, ref int remaining)
{ {
// Read nCodes and huffman.Valuess (and derive h.Length). // Read nCodes and huffman.Valuess (and derive h.Length).
// nCodes[i] is the number of codes with code length i. // nCodes[i] is the number of codes with code length i.
// h.Length is the total number of codes. // h.Length is the total number of codes.
@ -552,7 +547,7 @@ namespace ImageSharp.Formats
private byte DecodeHuffman(ref Huffman huffman) private byte DecodeHuffman(ref Huffman huffman)
{ {
// Copy stuff to the stack: // Copy stuff to the stack:
if (huffman.Length == 0) if (huffman.Length == 0)
{ {
throw new ImageFormatException("Uninitialized Huffman table"); throw new ImageFormatException("Uninitialized Huffman table");
@ -560,35 +555,24 @@ namespace ImageSharp.Formats
if (this.bits.UnreadBits < 8) if (this.bits.UnreadBits < 8)
{ {
//try var errorCode = this.bits.EnsureNBits(8, this);
//{
var errorCode = bits.EnsureNBits(8, this);
if (errorCode == ErrorCodes.NoError) if (errorCode == ErrorCodes.NoError)
{ {
ushort v = huffman.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff]; ushort v = huffman.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff];
if (v != 0) if (v != 0)
{
byte n = (byte)((v & 0xff) - 1);
this.bits.UnreadBits -= n;
this.bits.Mask >>= n;
return (byte)(v >> 8);
}
}
else
{ {
this.UnreadByteStuffedByte(); byte n = (byte)((v & 0xff) - 1);
this.bits.UnreadBits -= n;
this.bits.Mask >>= n;
return (byte)(v >> 8);
} }
}
//} else
//catch (ShortHuffmanDataException) // TODO: This is actually never thrown! {
//{ this.UnreadByteStuffedByte();
// if (this.bytes.UnreadableBytes != 0) }
// {
// this.UnreadByteStuffedByte();
// }
//}
} }
int code = 0; int code = 0;
@ -666,37 +650,6 @@ namespace ImageSharp.Formats
return ret; return ret;
} }
/// <summary>
/// Fills up the bytes buffer from the underlying stream.
/// It should only be called when there are no unread bytes in bytes.
/// </summary>
//private void Fill()
//{
// if (this.bytes.I != this.bytes.J)
// {
// throw new ImageFormatException("Fill called when unread bytes exist.");
// }
// // Move the last 2 bytes to the start of the buffer, in case we need
// // to call UnreadByteStuffedByte.
// if (this.bytes.J > 2)
// {
// this.bytes.Buffer[0] = this.bytes.Buffer[this.bytes.J - 2];
// this.bytes.Buffer[1] = this.bytes.Buffer[this.bytes.J - 1];
// this.bytes.I = 2;
// this.bytes.J = 2;
// }
// // Fill in the rest of the buffer.
// int n = this.inputStream.Read(this.bytes.Buffer, this.bytes.J, this.bytes.Buffer.Length - this.bytes.J);
// if (n == 0)
// {
// throw new EOFException();
// }
// this.bytes.J += n;
//}
/// <summary> /// <summary>
/// Undoes the most recent ReadByteStuffedByte call, /// Undoes the most recent ReadByteStuffedByte call,
/// giving a byte of data back from bits to bytes. The Huffman look-up table /// giving a byte of data back from bits to bytes. The Huffman look-up table
@ -716,7 +669,6 @@ namespace ImageSharp.Formats
} }
} }
/// <summary> /// <summary>
/// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing. /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
/// </summary> /// </summary>
@ -724,74 +676,9 @@ namespace ImageSharp.Formats
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByte() internal byte ReadByte()
{ {
return bytes.ReadByte(inputStream); return this.bytes.ReadByte(this.inputStream);
//while (this.bytes.I == this.bytes.J)
//{
// this.Fill();
//}
//byte x = this.bytes.Buffer[this.bytes.I];
//this.bytes.I++;
//this.bytes.UnreadableBytes = 0;
//return x;
} }
/// <summary>
/// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data.
/// </summary>
/// <returns>The <see cref="byte"/></returns>
//internal byte ReadByteStuffedByte(out ErrorCodes errorCode)
//{
// byte x;
// errorCode = ErrorCodes.NoError;
// // Take the fast path if bytes.buf contains at least two bytes.
// if (this.bytes.I + 2 <= this.bytes.J)
// {
// x = this.bytes.Buffer[this.bytes.I];
// this.bytes.I++;
// this.bytes.UnreadableBytes = 1;
// if (x != JpegConstants.Markers.XFF)
// {
// return x;
// }
// if (this.bytes.Buffer[this.bytes.I] != 0x00)
// {
// errorCode = ErrorCodes.MissingFF00;
// return 0;
// //throw new MissingFF00Exception();
// }
// this.bytes.I++;
// this.bytes.UnreadableBytes = 2;
// return JpegConstants.Markers.XFF;
// }
// this.bytes.UnreadableBytes = 0;
// x = this.ReadByte();
// this.bytes.UnreadableBytes = 1;
// if (x != JpegConstants.Markers.XFF)
// {
// return x;
// }
// x = this.ReadByte();
// this.bytes.UnreadableBytes = 2;
// if (x != 0x00)
// {
// errorCode = ErrorCodes.MissingFF00;
// return 0;
// //throw new MissingFF00Exception();
// }
// return JpegConstants.Markers.XFF;
//}
/// <summary> /// <summary>
/// Reads exactly length bytes into data. It does not care about byte stuffing. /// Reads exactly length bytes into data. It does not care about byte stuffing.
/// </summary> /// </summary>
@ -805,7 +692,7 @@ namespace ImageSharp.Formats
{ {
if (this.bits.UnreadBits >= 8) if (this.bits.UnreadBits >= 8)
{ {
UnreadByteStuffedByte(); this.UnreadByteStuffedByte();
} }
this.bytes.UnreadableBytes = 0; this.bytes.UnreadableBytes = 0;
@ -826,7 +713,7 @@ namespace ImageSharp.Formats
length -= this.bytes.J - this.bytes.I; length -= this.bytes.J - this.bytes.I;
this.bytes.I += this.bytes.J - this.bytes.I; this.bytes.I += this.bytes.J - this.bytes.I;
this.bytes.Fill(inputStream); this.bytes.Fill(this.inputStream);
} }
} }
} }
@ -863,7 +750,7 @@ namespace ImageSharp.Formats
break; break;
} }
this.bytes.Fill(inputStream); this.bytes.Fill(this.inputStream);
} }
} }
@ -988,7 +875,8 @@ namespace ImageSharp.Formats
case 1: case 1:
{ {
// Cb. // Cb.
if (this.componentArray[0].HorizontalFactor % h != 0 || this.componentArray[0].VerticalFactor % v != 0) if (this.componentArray[0].HorizontalFactor % h != 0
|| this.componentArray[0].VerticalFactor % v != 0)
{ {
throw new ImageFormatException("Unsupported subsampling ratio"); throw new ImageFormatException("Unsupported subsampling ratio");
} }
@ -999,7 +887,8 @@ namespace ImageSharp.Formats
case 2: case 2:
{ {
// Cr. // Cr.
if (this.componentArray[1].HorizontalFactor != h || this.componentArray[1].VerticalFactor != v) if (this.componentArray[1].HorizontalFactor != h
|| this.componentArray[1].VerticalFactor != v)
{ {
throw new ImageFormatException("Unsupported subsampling ratio"); throw new ImageFormatException("Unsupported subsampling ratio");
} }
@ -1039,7 +928,8 @@ namespace ImageSharp.Formats
break; break;
case 3: case 3:
if (this.componentArray[0].HorizontalFactor != h || this.componentArray[0].VerticalFactor != v) if (this.componentArray[0].HorizontalFactor != h
|| this.componentArray[0].VerticalFactor != v)
{ {
throw new ImageFormatException("Unsupported subsampling ratio"); throw new ImageFormatException("Unsupported subsampling ratio");
} }
@ -1157,11 +1047,8 @@ namespace ImageSharp.Formats
remaining -= 13; remaining -= 13;
// TODO: We should be using constants for this. // TODO: We should be using constants for this.
this.isJfif = this.temp[0] == 'J' && this.isJfif = this.temp[0] == 'J' && this.temp[1] == 'F' && this.temp[2] == 'I' && this.temp[3] == 'F'
this.temp[1] == 'F' && && this.temp[4] == '\x00';
this.temp[2] == 'I' &&
this.temp[3] == 'F' &&
this.temp[4] == '\x00';
if (this.isJfif) if (this.isJfif)
{ {
@ -1183,8 +1070,7 @@ namespace ImageSharp.Formats
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
/// <param name="image">The image.</param> /// <param name="image">The image.</param>
private void ProcessApp1Marker<TColor, TPacked>(int remaining, Image<TColor, TPacked> image) private void ProcessApp1Marker<TColor, TPacked>(int remaining, Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
where TPacked : struct
{ {
if (remaining < 6) if (remaining < 6)
{ {
@ -1195,12 +1081,8 @@ namespace ImageSharp.Formats
byte[] profile = new byte[remaining]; byte[] profile = new byte[remaining];
this.ReadFull(profile, 0, remaining); this.ReadFull(profile, 0, remaining);
if (profile[0] == 'E' && if (profile[0] == 'E' && profile[1] == 'x' && profile[2] == 'i' && profile[3] == 'f' && profile[4] == '\0'
profile[1] == 'x' && && profile[5] == '\0')
profile[2] == 'i' &&
profile[3] == 'f' &&
profile[4] == '\0' &&
profile[5] == '\0')
{ {
image.ExifProfile = new ExifProfile(profile); image.ExifProfile = new ExifProfile(profile);
} }
@ -1223,11 +1105,8 @@ namespace ImageSharp.Formats
this.ReadFull(this.temp, 0, 12); this.ReadFull(this.temp, 0, 12);
remaining -= 12; remaining -= 12;
if (this.temp[0] == 'A' && if (this.temp[0] == 'A' && this.temp[1] == 'd' && this.temp[2] == 'o' && this.temp[3] == 'b'
this.temp[1] == 'd' && && this.temp[4] == 'e')
this.temp[2] == 'o' &&
this.temp[3] == 'b' &&
this.temp[4] == 'e')
{ {
this.adobeTransformValid = true; this.adobeTransformValid = true;
this.adobeTransform = this.temp[11]; this.adobeTransform = this.temp[11];
@ -1248,12 +1127,12 @@ namespace ImageSharp.Formats
/// <param name="height">The image height.</param> /// <param name="height">The image height.</param>
/// <param name="image">The image.</param> /// <param name="image">The image.</param>
private void ConvertFromCmyk<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image) private void ConvertFromCmyk<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
where TPacked : struct
{ {
if (!this.adobeTransformValid) if (!this.adobeTransformValid)
{ {
throw new ImageFormatException("Unknown color model: 4-component JPEG doesn't have Adobe APP14 metadata"); throw new ImageFormatException(
"Unknown color model: 4-component JPEG doesn't have Adobe APP14 metadata");
} }
// If the 4-component JPEG image isn't explicitly marked as "Unknown (RGB // If the 4-component JPEG image isn't explicitly marked as "Unknown (RGB
@ -1274,21 +1153,21 @@ namespace ImageSharp.Formats
0, 0,
height, height,
y => y =>
{
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
for (int x = 0; x < width; x++)
{ {
byte yy = this.ycbcrImage.YChannel[yo + x]; int yo = this.ycbcrImage.GetRowYOffset(y);
byte cb = this.ycbcrImage.CbChannel[co + (x / scale)]; int co = this.ycbcrImage.GetRowCOffset(y);
byte cr = this.ycbcrImage.CrChannel[co + (x / scale)];
TColor packed = default(TColor); for (int x = 0; x < width; x++)
this.PackCmyk<TColor, TPacked>(ref packed, yy, cb, cr, x, y); {
pixels[x, y] = packed; byte yy = this.ycbcrImage.YChannel[yo + x];
} byte cb = this.ycbcrImage.CbChannel[co + (x / scale)];
}); byte cr = this.ycbcrImage.CrChannel[co + (x / scale)];
TColor packed = default(TColor);
this.PackCmyk<TColor, TPacked>(ref packed, yy, cb, cr, x, y);
pixels[x, y] = packed;
}
});
} }
this.AssignResolution(image); this.AssignResolution(image);
@ -1304,8 +1183,7 @@ namespace ImageSharp.Formats
/// <param name="height">The image height.</param> /// <param name="height">The image height.</param>
/// <param name="image">The image.</param> /// <param name="image">The image.</param>
private void ConvertFromGrayScale<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image) private void ConvertFromGrayScale<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
where TPacked : struct
{ {
image.InitPixels(width, height); image.InitPixels(width, height);
@ -1316,17 +1194,17 @@ namespace ImageSharp.Formats
height, height,
Bootstrapper.Instance.ParallelOptions, Bootstrapper.Instance.ParallelOptions,
y => y =>
{
int yoff = this.grayImage.GetRowOffset(y);
for (int x = 0; x < width; x++)
{ {
byte rgb = this.grayImage.Pixels[yoff + x]; int yoff = this.grayImage.GetRowOffset(y);
for (int x = 0; x < width; x++)
{
byte rgb = this.grayImage.Pixels[yoff + x];
TColor packed = default(TColor); TColor packed = default(TColor);
packed.PackFromBytes(rgb, rgb, rgb, 255); packed.PackFromBytes(rgb, rgb, rgb, 255);
pixels[x, y] = packed; pixels[x, y] = packed;
} }
}); });
} }
this.AssignResolution(image); this.AssignResolution(image);
@ -1341,8 +1219,7 @@ namespace ImageSharp.Formats
/// <param name="height">The image height.</param> /// <param name="height">The image height.</param>
/// <param name="image">The image.</param> /// <param name="image">The image.</param>
private void ConvertFromYCbCr<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image) private void ConvertFromYCbCr<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
where TPacked : struct
{ {
int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor; int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor;
image.InitPixels(width, height); image.InitPixels(width, height);
@ -1354,27 +1231,26 @@ namespace ImageSharp.Formats
height, height,
Bootstrapper.Instance.ParallelOptions, Bootstrapper.Instance.ParallelOptions,
y => y =>
{
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
for (int x = 0; x < width; x++)
{ {
byte yy = this.ycbcrImage.YChannel[yo + x]; int yo = this.ycbcrImage.GetRowYOffset(y);
byte cb = this.ycbcrImage.CbChannel[co + (x / scale)]; int co = this.ycbcrImage.GetRowCOffset(y);
byte cr = this.ycbcrImage.CrChannel[co + (x / scale)];
TColor packed = default(TColor); for (int x = 0; x < width; x++)
PackYcbCr<TColor, TPacked>(ref packed, yy, cb, cr); {
pixels[x, y] = packed; byte yy = this.ycbcrImage.YChannel[yo + x];
} byte cb = this.ycbcrImage.CbChannel[co + (x / scale)];
}); byte cr = this.ycbcrImage.CrChannel[co + (x / scale)];
TColor packed = default(TColor);
PackYcbCr<TColor, TPacked>(ref packed, yy, cb, cr);
pixels[x, y] = packed;
}
});
} }
this.AssignResolution(image); this.AssignResolution(image);
} }
/// <summary> /// <summary>
/// Converts the image from the original RBG image pixels. /// Converts the image from the original RBG image pixels.
/// </summary> /// </summary>
@ -1384,8 +1260,7 @@ namespace ImageSharp.Formats
/// <param name="height">The height.</param> /// <param name="height">The height.</param>
/// <param name="image">The image.</param> /// <param name="image">The image.</param>
private void ConvertFromRGB<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image) private void ConvertFromRGB<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
where TPacked : struct
{ {
int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor; int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor;
image.InitPixels(width, height); image.InitPixels(width, height);
@ -1397,21 +1272,21 @@ namespace ImageSharp.Formats
height, height,
Bootstrapper.Instance.ParallelOptions, Bootstrapper.Instance.ParallelOptions,
y => y =>
{
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
for (int x = 0; x < width; x++)
{ {
byte red = this.ycbcrImage.YChannel[yo + x]; int yo = this.ycbcrImage.GetRowYOffset(y);
byte green = this.ycbcrImage.CbChannel[co + (x / scale)]; int co = this.ycbcrImage.GetRowCOffset(y);
byte blue = this.ycbcrImage.CrChannel[co + (x / scale)];
TColor packed = default(TColor); for (int x = 0; x < width; x++)
packed.PackFromBytes(red, green, blue, 255); {
pixels[x, y] = packed; byte red = this.ycbcrImage.YChannel[yo + x];
} byte green = this.ycbcrImage.CbChannel[co + (x / scale)];
}); byte blue = this.ycbcrImage.CrChannel[co + (x / scale)];
TColor packed = default(TColor);
packed.PackFromBytes(red, green, blue, 255);
pixels[x, y] = packed;
}
});
} }
this.AssignResolution(image); this.AssignResolution(image);
@ -1424,8 +1299,7 @@ namespace ImageSharp.Formats
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="image">The image to assign the resolution to.</param> /// <param name="image">The image to assign the resolution to.</param>
private void AssignResolution<TColor, TPacked>(Image<TColor, TPacked> image) private void AssignResolution<TColor, TPacked>(Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
where TPacked : struct
{ {
if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0) if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0)
{ {
@ -1476,7 +1350,7 @@ namespace ImageSharp.Formats
for (int i = 0; i < scanComponentCount; i++) for (int i = 0; i < scanComponentCount; i++)
{ {
ProcessScanImpl(i, ref scan[i], scan, ref totalHv); this.ProcessScanImpl(i, ref scan[i], scan, ref totalHv);
} }
// Section B.2.3 states that if there is more than one component then the // Section B.2.3 states that if there is more than one component then the
@ -1546,7 +1420,8 @@ namespace ImageSharp.Formats
int compIndex = scan[i].Index; int compIndex = scan[i].Index;
if (this.progCoeffs[compIndex] == null) if (this.progCoeffs[compIndex] == null)
{ {
var size = mxx * myy * this.componentArray[compIndex].HorizontalFactor * this.componentArray[compIndex].VerticalFactor; var size = mxx * myy * this.componentArray[compIndex].HorizontalFactor
* this.componentArray[compIndex].VerticalFactor;
this.progCoeffs[compIndex] = new Block8x8F[size]; this.progCoeffs[compIndex] = new Block8x8F[size];
} }
@ -1573,7 +1448,7 @@ namespace ImageSharp.Formats
// Tricky way to copy contents of the Unzig static variable to the stack: // Tricky way to copy contents of the Unzig static variable to the stack:
StackallocUnzigData unzigOnStack = new StackallocUnzigData(); StackallocUnzigData unzigOnStack = new StackallocUnzigData();
int* unzigPtr = unzigOnStack.Data; int* unzigPtr = unzigOnStack.Data;
Marshal.Copy(Unzig, 0, (IntPtr) unzigPtr, 64); Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64);
for (int my = 0; my < myy; my++) for (int my = 0; my < myy; my++)
{ {
@ -1585,7 +1460,6 @@ namespace ImageSharp.Formats
int hi = this.componentArray[compIndex].HorizontalFactor; int hi = this.componentArray[compIndex].HorizontalFactor;
int vi = this.componentArray[compIndex].VerticalFactor; int vi = this.componentArray[compIndex].VerticalFactor;
for (int j = 0; j < hi * vi; j++) for (int j = 0; j < hi * vi; j++)
{ {
// The blocks are traversed one MCU at a time. For 4:2:0 chroma // The blocks are traversed one MCU at a time. For 4:2:0 chroma
@ -1630,43 +1504,59 @@ namespace ImageSharp.Formats
var qtIndex = this.componentArray[compIndex].Selector; var qtIndex = this.componentArray[compIndex].Selector;
// TODO: Find a way to clean up this mess // TODO: Find a way to clean up this mess
fixed (Block8x8F* qtp = &this.quantizationTables[qtIndex]) fixed (Block8x8F* qtp = &this.quantizationTables[qtIndex])
{ {
if (this.isProgressive) if (this.isProgressive)
// Load the previous partially decoded coefficients, if applicable. // Load the previous partially decoded coefficients, if applicable.
{ {
blockIndex = ((@by*mxx)*hi) + bx; this.blockIndex = ((@by * mxx) * hi) + bx;
fixed (Block8x8F* bp = &this.progCoeffs[compIndex][blockIndex]) fixed (Block8x8F* bp = &this.progCoeffs[compIndex][this.blockIndex])
{ {
ProcessBlockImpl(ah, this.ProcessBlockImpl(
ah,
bp, bp,
&temp1, &temp1,
&temp2, &temp2,
unzigPtr, unzigPtr,
scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi, bx, scan,
qtp i,
); zigStart,
zigEnd,
al,
dc,
compIndex,
@by,
mxx,
hi,
bx,
qtp);
} }
} }
else else
{ {
b.Clear(); b.Clear();
ProcessBlockImpl(ah, this.ProcessBlockImpl(
ah,
&b, &b,
&temp1, &temp1,
&temp2, &temp2,
unzigPtr, unzigPtr,
scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi, scan,
bx, qtp i,
); zigStart,
zigEnd,
al,
dc,
compIndex,
@by,
mxx,
hi,
bx,
qtp);
} }
} }
} }
// for j // for j
@ -1709,14 +1599,22 @@ namespace ImageSharp.Formats
} }
private void ProcessBlockImpl( private void ProcessBlockImpl(
int ah, int ah,
Block8x8F* b, Block8x8F* b,
Block8x8F* temp1, Block8x8F* temp1,
Block8x8F* temp2, Block8x8F* temp2,
int* unzigPtr, int* unzigPtr,
Scan[] scan, Scan[] scan,
int i, int zigStart, int zigEnd, int al, int i,
int[] dc, int compIndex, int @by, int mxx, int hi, int bx, int zigStart,
int zigEnd,
int al,
int[] dc,
int compIndex,
int @by,
int mxx,
int hi,
int bx,
Block8x8F* qt) Block8x8F* qt)
{ {
var huffmannIdx = AcTable * ThRowSize + scan[i].AcTableSelector; var huffmannIdx = AcTable * ThRowSize + scan[i].AcTableSelector;
@ -1732,7 +1630,8 @@ namespace ImageSharp.Formats
zig++; zig++;
// Decode the DC coefficient, as specified in section F.2.2.1. // Decode the DC coefficient, as specified in section F.2.2.1.
byte value = this.DecodeHuffman(ref this.huffmanTrees[DcTable * ThRowSize + scan[i].DcTableSelector]); byte value = this.DecodeHuffman(
ref this.huffmanTrees[DcTable * ThRowSize + scan[i].DcTableSelector]);
if (value > 16) if (value > 16)
{ {
throw new ImageFormatException("Excessive DC component"); throw new ImageFormatException("Excessive DC component");
@ -1741,7 +1640,7 @@ namespace ImageSharp.Formats
int deltaDC = this.bits.ReceiveExtend(value, this); int deltaDC = this.bits.ReceiveExtend(value, this);
dc[compIndex] += deltaDC; dc[compIndex] += deltaDC;
//b[0] = dc[compIndex] << al; // b[0] = dc[compIndex] << al;
Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al); Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al);
} }
@ -1752,7 +1651,7 @@ namespace ImageSharp.Formats
else else
{ {
// Decode the AC coefficients, as specified in section F.2.2.2. // Decode the AC coefficients, as specified in section F.2.2.2.
//Huffman huffv = ; // Huffman huffv = ;
for (; zig <= zigEnd; zig++) for (; zig <= zigEnd; zig++)
{ {
byte value = this.DecodeHuffman(ref this.huffmanTrees[huffmannIdx]); byte value = this.DecodeHuffman(ref this.huffmanTrees[huffmannIdx]);
@ -1768,7 +1667,7 @@ namespace ImageSharp.Formats
int ac = this.bits.ReceiveExtend(val1, this); int ac = this.bits.ReceiveExtend(val1, this);
//b[Unzig[zig]] = ac << al; // b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al); Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al);
} }
else else
@ -1813,8 +1712,8 @@ namespace ImageSharp.Formats
// Dequantize, perform the inverse DCT and store the block to the image. // Dequantize, perform the inverse DCT and store the block to the image.
Block8x8F.UnZig(b, qt, unzigPtr); Block8x8F.UnZig(b, qt, unzigPtr);
b->IDCTInto(ref *temp1, ref *temp2); b->TransformIDCTInto(ref *temp1, ref *temp2);
byte[] dst; byte[] dst;
int offset; int offset;
@ -1861,10 +1760,10 @@ namespace ImageSharp.Formats
} }
// Level shift by +128, clip to [0, 255], and write to dst. // Level shift by +128, clip to [0, 255], and write to dst.
temp1->CopyColorsTo(new MutableSpan<byte>(dst, offset), stride, temp2); temp1->CopyColorsTo(new MutableSpan<byte>(dst, offset), stride, temp2);
} }
private void ProcessScanImpl(int i, ref Scan currentScan, Scan[] scan, ref int totalHv) private void ProcessScanImpl(int i, ref Scan currentScan, Scan[] scan, ref int totalHv)
{ {
// Component selector. // Component selector.
@ -1886,10 +1785,15 @@ namespace ImageSharp.Formats
currentScan.Index = (byte)compIndex; currentScan.Index = (byte)compIndex;
ProcessComponentImpl(i, ref currentScan, scan, ref totalHv, ref this.componentArray[compIndex]); this.ProcessComponentImpl(i, ref currentScan, scan, ref totalHv, ref this.componentArray[compIndex]);
} }
private void ProcessComponentImpl(int i, ref Scan currentScan, Scan[] scan, ref int totalHv, ref Component currentComponent) private void ProcessComponentImpl(
int i,
ref Scan currentScan,
Scan[] scan,
ref int totalHv,
ref Component currentComponent)
{ {
// Section B.2.3 states that "the value of Cs_j shall be different from // Section B.2.3 states that "the value of Cs_j shall be different from
// the values of Cs_1 through Cs_(j-1)". Since we have previously // the values of Cs_1 through Cs_(j-1)". Since we have previously
@ -1904,7 +1808,6 @@ namespace ImageSharp.Formats
} }
} }
totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor;
currentScan.DcTableSelector = (byte)(this.temp[2 + (2 * i)] >> 4); currentScan.DcTableSelector = (byte)(this.temp[2 + (2 * i)] >> 4);
@ -1925,6 +1828,7 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <param name="b">The block of coefficients</param> /// <param name="b">The block of coefficients</param>
/// <param name="h">The Huffman tree</param> /// <param name="h">The Huffman tree</param>
/// <param name="unzigPtr"></param>
/// <param name="zigStart">The zig-zag start index</param> /// <param name="zigStart">The zig-zag start index</param>
/// <param name="zigEnd">The zig-zag end index</param> /// <param name="zigEnd">The zig-zag end index</param>
/// <param name="delta">The low transform offset</param> /// <param name="delta">The low transform offset</param>
@ -1941,11 +1845,11 @@ namespace ImageSharp.Formats
bool bit = this.DecodeBit(); bool bit = this.DecodeBit();
if (bit) if (bit)
{ {
int stuff = (int) Block8x8F.GetScalarAt(b, 0); int stuff = (int)Block8x8F.GetScalarAt(b, 0);
//int stuff = (int)b[0]; // int stuff = (int)b[0];
stuff |= delta; stuff |= delta;
//b[0] = stuff; // b[0] = stuff;
Block8x8F.SetScalarAt(b, 0, stuff); Block8x8F.SetScalarAt(b, 0, stuff);
} }
@ -2145,7 +2049,8 @@ namespace ImageSharp.Formats
return true; return true;
} }
return this.componentArray[0].Identifier == 'R' && this.componentArray[1].Identifier == 'G' && this.componentArray[2].Identifier == 'B'; return this.componentArray[0].Identifier == 'R' && this.componentArray[1].Identifier == 'G'
&& this.componentArray[2].Identifier == 'B';
} }
/// <summary> /// <summary>
@ -2160,8 +2065,7 @@ namespace ImageSharp.Formats
/// <param name="cr">The cr chroma component.</param> /// <param name="cr">The cr chroma component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void PackYcbCr<TColor, TPacked>(ref TColor packed, byte y, byte cb, byte cr) private static void PackYcbCr<TColor, TPacked>(ref TColor packed, byte y, byte cb, byte cr)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
where TPacked : struct
{ {
int ccb = cb - 128; int ccb = cb - 128;
int ccr = cr - 128; int ccr = cr - 128;
@ -2186,8 +2090,7 @@ namespace ImageSharp.Formats
/// <param name="xx">The x-position within the image.</param> /// <param name="xx">The x-position within the image.</param>
/// <param name="yy">The y-position within the image.</param> /// <param name="yy">The y-position within the image.</param>
private void PackCmyk<TColor, TPacked>(ref TColor packed, byte y, byte cb, byte cr, int xx, int yy) private void PackCmyk<TColor, TPacked>(ref TColor packed, byte y, byte cb, byte cr, int xx, int yy)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
where TPacked : struct
{ {
// TODO: We can speed this up further with Vector4 // TODO: We can speed this up further with Vector4
int ccb = cb - 128; int ccb = cb - 128;
@ -2265,11 +2168,11 @@ namespace ImageSharp.Formats
public void Dispose() public void Dispose()
{ {
for (int i = 0; i < huffmanTrees.Length; i++) for (int i = 0; i < this.huffmanTrees.Length; i++)
{ {
huffmanTrees[i].Dispose(); this.huffmanTrees[i].Dispose();
} }
bytes.Dispose(); this.bytes.Dispose();
} }
} }
} }

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

@ -31,7 +31,7 @@ namespace ImageSharp.Tests.Formats.Jpg
public void Indexer() public void Indexer()
{ {
float sum = 0; float sum = 0;
Measure(Times, () => this.Measure(Times, () =>
{ {
Block8x8F block = new Block8x8F(); Block8x8F block = new Block8x8F();
@ -44,8 +44,8 @@ namespace ImageSharp.Tests.Formats.Jpg
{ {
sum += block[i]; sum += block[i];
} }
}); });
Assert.Equal(sum, 64f*63f*0.5f); Assert.Equal(sum, 64f * 63f * 0.5f);
} }
[Fact] [Fact]
@ -151,28 +151,12 @@ namespace ImageSharp.Tests.Formats.Jpg
v.CopyTo(mirror); v.CopyTo(mirror);
}); });
Assert.Equal(data, mirror); Assert.Equal(data, mirror);
//PrintLinearData((MutableSpan<int>)mirror); //PrintLinearData((MutableSpan<int>)mirror);
} }
[Fact]
public void TransposeInplace()
{
float[] expected = Create8x8FloatData();
ReferenceImplementations.Transpose8x8(expected);
Block8x8F buffer = new Block8x8F();
buffer.LoadFrom(Create8x8FloatData());
buffer.TransposeInplace();
float[] actual = new float[64];
buffer.CopyTo(actual);
Assert.Equal(expected, actual);
}
[Fact] [Fact]
public void TransposeInto() public void TransposeInto()
{ {
@ -231,7 +215,7 @@ namespace ImageSharp.Tests.Formats.Jpg
Block8x8F dest = new Block8x8F(); Block8x8F dest = new Block8x8F();
source.iDCT2D8x4_LeftPart(ref dest); source.IDCT8x4_LeftPart(ref dest);
float[] actualDestArray = new float[64]; float[] actualDestArray = new float[64];
dest.CopyTo(actualDestArray); dest.CopyTo(actualDestArray);
@ -256,7 +240,7 @@ namespace ImageSharp.Tests.Formats.Jpg
Block8x8F dest = new Block8x8F(); Block8x8F dest = new Block8x8F();
source.iDCT2D8x4_RightPart(ref dest); source.IDCT8x4_RightPart(ref dest);
float[] actualDestArray = new float[64]; float[] actualDestArray = new float[64];
dest.CopyTo(actualDestArray); dest.CopyTo(actualDestArray);
@ -302,7 +286,7 @@ namespace ImageSharp.Tests.Formats.Jpg
Block8x8F dest = new Block8x8F(); Block8x8F dest = new Block8x8F();
Block8x8F tempBuffer = new Block8x8F(); Block8x8F tempBuffer = new Block8x8F();
source.IDCTInto(ref dest, ref tempBuffer); source.TransformIDCTInto(ref dest, ref tempBuffer);
float[] actualDestArray = new float[64]; float[] actualDestArray = new float[64];
dest.CopyTo(actualDestArray); dest.CopyTo(actualDestArray);
@ -342,26 +326,7 @@ namespace ImageSharp.Tests.Formats.Jpg
Assert.Equal(colorsExpected, colorsActual); Assert.Equal(colorsExpected, colorsActual);
} }
[Fact]
public void CropInto()
{
Block8x8F block = new Block8x8F();
block.LoadFrom(Create8x8FloatData());
Block8x8F dest = new Block8x8F();
block.CropInto(10, 20, ref dest);
float[] array = new float[64];
dest.CopyTo(array);
PrintLinearData(array);
foreach (float val in array)
{
Assert.InRange(val, 10, 20);
}
}
private static float[] Create8x8ColorCropTestData() private static float[] Create8x8ColorCropTestData()
{ {
float[] result = new float[64]; float[] result = new float[64];
@ -376,7 +341,7 @@ namespace ImageSharp.Tests.Formats.Jpg
} }
[Fact] [Fact]
public void ColorifyInto() public void TransformByteConvetibleColorValuesInto()
{ {
Block8x8F block = new Block8x8F(); Block8x8F block = new Block8x8F();
var input = Create8x8ColorCropTestData(); var input = Create8x8ColorCropTestData();
@ -386,7 +351,7 @@ namespace ImageSharp.Tests.Formats.Jpg
Block8x8F dest = new Block8x8F(); Block8x8F dest = new Block8x8F();
block.ColorifyInto(ref dest); block.TransformByteConvetibleColorValuesInto(ref dest);
float[] array = new float[64]; float[] array = new float[64];
dest.CopyTo(array); dest.CopyTo(array);

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

@ -1,46 +1,57 @@
using System; // ReSharper disable InconsistentNaming
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using ImageSharp.Formats;
// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests.Formats.Jpg namespace ImageSharp.Tests.Formats.Jpg
{ {
using System.Numerics;
using System.Runtime.CompilerServices;
using ImageSharp.Formats;
/// <summary> /// <summary>
/// This class contains simplified (unefficient) reference implementations to produce verification data for unit tests /// This class contains simplified (unefficient) reference implementations to produce verification data for unit tests
/// DCT code Ported from https://github.com/norishigefukushima/dct_simd /// DCT code Ported from https://github.com/norishigefukushima/dct_simd
/// </summary> /// </summary>
internal static class ReferenceImplementations internal static class ReferenceImplementations
{ {
/// <summary>
/// Transpose 8x8 block stored linearly in a span (inplace)
/// </summary>
/// <param name="data"></param>
internal static void Transpose8x8(MutableSpan<float> data) internal static void Transpose8x8(MutableSpan<float> data)
{ {
for (int i = 1; i < 8; i++) for (int i = 1; i < 8; i++)
{ {
int i8 = i*8; int i8 = i * 8;
for (int j = 0; j < i; j++) for (int j = 0; j < i; j++)
{ {
float tmp = data[i8 + j]; float tmp = data[i8 + j];
data[i8 + j] = data[j*8 + i]; data[i8 + j] = data[j * 8 + i];
data[j*8 + i] = tmp; data[j * 8 + i] = tmp;
} }
} }
} }
/// <summary>
/// Transpose 8x8 block stored linearly in a span
/// </summary>
internal static void Transpose8x8(MutableSpan<float> src, MutableSpan<float> dest) internal static void Transpose8x8(MutableSpan<float> src, MutableSpan<float> dest)
{ {
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
int i8 = i*8; int i8 = i * 8;
for (int j = 0; j < 8; j++) for (int j = 0; j < 8; j++)
{ {
dest[j*8 + i] = src[i8 + j]; dest[j * 8 + i] = src[i8 + j];
} }
} }
} }
internal static void iDCT1Dllm_32f(MutableSpan<float> y, MutableSpan<float> x) /// <summary>
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L200
/// </summary>
/// <param name="y"></param>
/// <param name="x"></param>
private static void iDCT1Dllm_32f(MutableSpan<float> y, MutableSpan<float> x)
{ {
float a0, a1, a2, a3, b0, b1, b2, b3; float a0, a1, a2, a3, b0, b1, b2, b3;
float z0, z1, z2, z3, z4; float z0, z1, z2, z3, z4;
@ -58,23 +69,23 @@ namespace ImageSharp.Tests.Formats.Jpg
z1 = y[3] + y[5]; z1 = y[3] + y[5];
z2 = y[3] + y[7]; z2 = y[3] + y[7];
z3 = y[1] + y[5]; z3 = y[1] + y[5];
z4 = (z0 + z1)*r3; z4 = (z0 + z1) * r3;
z0 = z0*(-r3 + r7); z0 = z0 * (-r3 + r7);
z1 = z1*(-r3 - r1); z1 = z1 * (-r3 - r1);
z2 = z2*(-r3 - r5) + z4; z2 = z2 * (-r3 - r5) + z4;
z3 = z3*(-r3 + r5) + z4; z3 = z3 * (-r3 + r5) + z4;
b3 = y[7]*(-r1 + r3 + r5 - r7) + z0 + z2; b3 = y[7] * (-r1 + r3 + r5 - r7) + z0 + z2;
b2 = y[5]*(r1 + r3 - r5 + r7) + z1 + z3; b2 = y[5] * (r1 + r3 - r5 + r7) + z1 + z3;
b1 = y[3]*(r1 + r3 + r5 - r7) + z1 + z2; b1 = y[3] * (r1 + r3 + r5 - r7) + z1 + z2;
b0 = y[1]*(r1 + r3 - r5 - r7) + z0 + z3; b0 = y[1] * (r1 + r3 - r5 - r7) + z0 + z3;
z4 = (y[2] + y[6])*r6; z4 = (y[2] + y[6]) * r6;
z0 = y[0] + y[4]; z0 = y[0] + y[4];
z1 = y[0] - y[4]; z1 = y[0] - y[4];
z2 = z4 - y[6]*(r2 + r6); z2 = z4 - y[6] * (r2 + r6);
z3 = z4 + y[2]*(r2 - r6); z3 = z4 + y[2] * (r2 - r6);
a0 = z0 + z3; a0 = z0 + z3;
a3 = z0 - z3; a3 = z0 - z3;
a1 = z1 + z2; a1 = z1 + z2;
@ -90,20 +101,27 @@ namespace ImageSharp.Tests.Formats.Jpg
x[4] = a3 - b3; x[4] = a3 - b3;
} }
/// <summary>
/// Original: https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L239
/// Applyies IDCT transformation on "s" copying transformed values to "d", using temporal block "temp"
/// </summary>
/// <param name="s"></param>
/// <param name="d"></param>
/// <param name="temp"></param>
internal static void iDCT2D_llm(MutableSpan<float> s, MutableSpan<float> d, MutableSpan<float> temp) internal static void iDCT2D_llm(MutableSpan<float> s, MutableSpan<float> d, MutableSpan<float> temp)
{ {
int j; int j;
for (j = 0; j < 8; j++) for (j = 0; j < 8; j++)
{ {
iDCT1Dllm_32f(s.Slice(j*8), temp.Slice(j*8)); iDCT1Dllm_32f(s.Slice(j * 8), temp.Slice(j * 8));
} }
Transpose8x8(temp, d); Transpose8x8(temp, d);
for (j = 0; j < 8; j++) for (j = 0; j < 8; j++)
{ {
iDCT1Dllm_32f(d.Slice(j*8), temp.Slice(j*8)); iDCT1Dllm_32f(d.Slice(j * 8), temp.Slice(j * 8));
} }
Transpose8x8(temp, d); Transpose8x8(temp, d);
@ -113,8 +131,6 @@ namespace ImageSharp.Tests.Formats.Jpg
d[j] *= 0.125f; d[j] *= 0.125f;
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 _mm_load_ps(MutableSpan<float> src, int offset) private static Vector4 _mm_load_ps(MutableSpan<float> src, int offset)
@ -123,12 +139,6 @@ namespace ImageSharp.Tests.Formats.Jpg
return new Vector4(src[0], src[1], src[2], src[3]); return new Vector4(src[0], src[1], src[2], src[3]);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 _mm_load_ps(MutableSpan<float> src)
{
return new Vector4(src[0], src[1], src[2], src[3]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void _mm_store_ps(MutableSpan<float> dest, int offset, Vector4 src) private static void _mm_store_ps(MutableSpan<float> dest, int offset, Vector4 src)
{ {
@ -139,29 +149,37 @@ namespace ImageSharp.Tests.Formats.Jpg
dest[3] = src.W; dest[3] = src.W;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void _mm_store_ps(MutableSpan<float> dest, Vector4 src)
{
dest[0] = src.X;
dest[1] = src.Y;
dest[2] = src.Z;
dest[3] = src.W;
}
private static readonly Vector4 _1_175876 = new Vector4(1.175876f); private static readonly Vector4 _1_175876 = new Vector4(1.175876f);
private static readonly Vector4 _1_961571 = new Vector4(-1.961571f); private static readonly Vector4 _1_961571 = new Vector4(-1.961571f);
private static readonly Vector4 _0_390181 = new Vector4(-0.390181f); private static readonly Vector4 _0_390181 = new Vector4(-0.390181f);
private static readonly Vector4 _0_899976 = new Vector4(-0.899976f); private static readonly Vector4 _0_899976 = new Vector4(-0.899976f);
private static readonly Vector4 _2_562915 = new Vector4(-2.562915f); private static readonly Vector4 _2_562915 = new Vector4(-2.562915f);
private static readonly Vector4 _0_298631 = new Vector4(0.298631f); private static readonly Vector4 _0_298631 = new Vector4(0.298631f);
private static readonly Vector4 _2_053120 = new Vector4(2.053120f); private static readonly Vector4 _2_053120 = new Vector4(2.053120f);
private static readonly Vector4 _3_072711 = new Vector4(3.072711f); private static readonly Vector4 _3_072711 = new Vector4(3.072711f);
private static readonly Vector4 _1_501321 = new Vector4(1.501321f); private static readonly Vector4 _1_501321 = new Vector4(1.501321f);
private static readonly Vector4 _0_541196 = new Vector4(0.541196f); private static readonly Vector4 _0_541196 = new Vector4(0.541196f);
private static readonly Vector4 _1_847759 = new Vector4(-1.847759f); private static readonly Vector4 _1_847759 = new Vector4(-1.847759f);
private static readonly Vector4 _0_765367 = new Vector4(0.765367f); private static readonly Vector4 _0_765367 = new Vector4(0.765367f);
/// <summary>
/// Original:
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
/// Does a part of the IDCT job on the given parts of the blocks
/// </summary>
/// <param name="y"></param>
/// <param name="x"></param>
internal static void iDCT2D8x4_32f(MutableSpan<float> y, MutableSpan<float> x) internal static void iDCT2D8x4_32f(MutableSpan<float> y, MutableSpan<float> x)
{ {
/* /*
@ -178,7 +196,7 @@ namespace ImageSharp.Tests.Formats.Jpg
6: 6:
7: 0.275899 7: 0.275899
*/ */
Vector4 my1 = _mm_load_ps(y, 8); Vector4 my1 = _mm_load_ps(y, 8);
Vector4 my7 = _mm_load_ps(y, 56); Vector4 my7 = _mm_load_ps(y, 56);
Vector4 mz0 = my1 + my7; Vector4 mz0 = my1 + my7;
@ -188,16 +206,16 @@ namespace ImageSharp.Tests.Formats.Jpg
Vector4 my5 = _mm_load_ps(y, 40); Vector4 my5 = _mm_load_ps(y, 40);
Vector4 mz1 = my3 + my5; Vector4 mz1 = my3 + my5;
Vector4 mz3 = my1 + my5; Vector4 mz3 = my1 + my5;
Vector4 mz4 = ((mz0 + mz1)* _1_175876); Vector4 mz4 = ((mz0 + mz1) * _1_175876);
//z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5]; //z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5];
//z4 = (z0 + z1) * r[3]; //z4 = (z0 + z1) * r[3];
mz2 = mz2* _1_961571 + mz4; mz2 = mz2 * _1_961571 + mz4;
mz3 = mz3* _0_390181 + mz4; mz3 = mz3 * _0_390181 + mz4;
mz0 = mz0* _0_899976; mz0 = mz0 * _0_899976;
mz1 = mz1* _2_562915; mz1 = mz1 * _2_562915;
/* /*
-0.899976 -0.899976
-2.562915 -2.562915
@ -208,11 +226,10 @@ namespace ImageSharp.Tests.Formats.Jpg
z2 = z2 * (-r[3] - r[5]) + z4; z2 = z2 * (-r[3] - r[5]) + z4;
z3 = z3 * (-r[3] + r[5]) + z4;*/ z3 = z3 * (-r[3] + r[5]) + z4;*/
Vector4 mb3 = my7 * _0_298631 + mz0 + mz2;
Vector4 mb3 = my7* _0_298631 + mz0 + mz2; Vector4 mb2 = my5 * _2_053120 + mz1 + mz3;
Vector4 mb2 = my5* _2_053120 + mz1 + mz3; Vector4 mb1 = my3 * _3_072711 + mz1 + mz2;
Vector4 mb1 = my3* _3_072711 + mz1 + mz2; Vector4 mb0 = my1 * _1_501321 + mz0 + mz3;
Vector4 mb0 = my1* _1_501321 + mz0 + mz3;
/* /*
0.298631 0.298631
@ -227,14 +244,14 @@ namespace ImageSharp.Tests.Formats.Jpg
Vector4 my2 = _mm_load_ps(y, 16); Vector4 my2 = _mm_load_ps(y, 16);
Vector4 my6 = _mm_load_ps(y, 48); Vector4 my6 = _mm_load_ps(y, 48);
mz4 = (my2 + my6)* _0_541196; mz4 = (my2 + my6) * _0_541196;
Vector4 my0 = _mm_load_ps(y, 0); Vector4 my0 = _mm_load_ps(y, 0);
Vector4 my4 = _mm_load_ps(y, 32); Vector4 my4 = _mm_load_ps(y, 32);
mz0 = my0 + my4; mz0 = my0 + my4;
mz1 = my0 - my4; mz1 = my0 - my4;
mz2 = mz4 + my6* _1_847759; mz2 = mz4 + my6 * _1_847759;
mz3 = mz4 + my2* _0_765367; mz3 = mz4 + my2 * _0_765367;
my0 = mz0 + mz3; my0 = mz0 + mz3;
my3 = mz0 - mz3; my3 = mz0 - mz3;
@ -275,55 +292,12 @@ namespace ImageSharp.Tests.Formats.Jpg
*/ */
} }
internal static void iDCT8x8_llm_sse(MutableSpan<float> s, MutableSpan<float> d, MutableSpan<float> temp) /// <summary>
{ /// Copies color values from block to the destination image buffer.
Transpose8x8(s, temp); /// </summary>
iDCT2D8x4_32f(temp, d); /// <param name="block"></param>
/// <param name="buffer"></param>
iDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); /// <param name="stride"></param>
Transpose8x8(d, temp);
iDCT2D8x4_32f(temp, d);
iDCT2D8x4_32f(temp.Slice(4), d.Slice(4));
Vector4 c = new Vector4(0.1250f);
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//0
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//1
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//2
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//3
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//4
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//5
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//6
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//7
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//8
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//9
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//10
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//11
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//12
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//13
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//14
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//15
}
internal static unsafe void CopyColorsTo(ref Block8x8F block, MutableSpan<byte> buffer, int stride) internal static unsafe void CopyColorsTo(ref Block8x8F block, MutableSpan<byte> buffer, int stride)
{ {
fixed (Block8x8F* p = &block) fixed (Block8x8F* p = &block)
@ -356,9 +330,6 @@ namespace ImageSharp.Tests.Formats.Jpg
} }
} }
} }
} }
} }
} }
Loading…
Cancel
Save