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.
// </copyright>
using System.Runtime.CompilerServices;
namespace ImageSharp.Formats
{
using System.Runtime.CompilerServices;
/// <summary>
/// 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
@ -30,20 +30,20 @@ namespace ImageSharp.Formats
/// </summary>
public int UnreadBits;
/// <summary>
/// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at
/// 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.
/// </summary>
/// <param name="n">The number of bits to ensure.</param>
/// <param name="decoder"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal JpegDecoderCore.ErrorCodes EnsureNBits(int n, JpegDecoderCore decoder)
{
while (true)
{
JpegDecoderCore.ErrorCodes errorCode;
byte c = decoder.bytes.ReadByteStuffedByte(decoder.inputStream, out errorCode);
if (errorCode != JpegDecoderCore.ErrorCodes.NoError)
@ -51,40 +51,39 @@ namespace ImageSharp.Formats
return errorCode;
}
Accumulator = (Accumulator << 8) | c;
UnreadBits += 8;
if (Mask == 0)
this.Accumulator = (this.Accumulator << 8) | c;
this.UnreadBits += 8;
if (this.Mask == 0)
{
Mask = 1 << 7;
this.Mask = 1 << 7;
}
else
{
Mask <<= 8;
this.Mask <<= 8;
}
if (UnreadBits >= n)
if (this.UnreadBits >= n)
{
return JpegDecoderCore.ErrorCodes.NoError;
//break;
}
}
}
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)
{
throw new JpegDecoderCore.MissingFF00Exception();
}
}
UnreadBits -= t;
Mask >>= t;
this.UnreadBits -= t;
this.Mask >>= 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))
{
@ -93,7 +92,5 @@ namespace ImageSharp.Formats
return x;
}
}
}

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

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

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

@ -1,4 +1,5 @@

// <auto-generated />
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -8,6 +9,14 @@ namespace ImageSharp.Formats
{
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)]
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;
}
public void CropInto(float min, float max, ref Block8x8F d)
{
Vector4 minVec = new Vector4(min);
Vector4 maxVec = new Vector4(max);
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)
/// <summary>
/// Level shift by +128, clip to [0, 255]
/// </summary>
/// <param name="d">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d)
{
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;

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

@ -4,19 +4,28 @@
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
// <auto-generated />
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
<#
char[] coordz = new[] {'X', 'Y', 'Z', 'W'};
char[] coordz = {'X', 'Y', 'Z', 'W'};
#>
namespace ImageSharp.Formats
{
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)]
public void TransposeInto(ref Block8x8F d)
{
@ -34,33 +43,7 @@ namespace ImageSharp.Formats
char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R';
string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord}; ";
//bld.Append(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("");
}
@ -68,8 +51,12 @@ namespace ImageSharp.Formats
#>
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ColorifyInto(ref Block8x8F d)
/// <summary>
/// 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;
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// <copyright file="Block8x8F.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// ReSharper disable InconsistentNaming
namespace ImageSharp.Formats
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// DCT code Ported from https://github.com/norishigefukushima/dct_simd
/// </summary>
@ -37,251 +39,193 @@ namespace ImageSharp.Formats
public Vector4 V7L;
public Vector4 V7R;
public const int VectorCount = 16;
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)]
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);
//float* fp = (float*)ptr;
//for (int i = 0; i < ScalarCount; i++)
//{
// fp[i] = source[i];
//}
Marshal.Copy(source.Data, source.Offset, (IntPtr)ptr, ScalarCount);
}
}
/// <summary>
/// Load raw 32bit floating point data from source
/// </summary>
[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)]
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)]
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)]
public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan<float> dest)
{
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];
}
}
Marshal.Copy((IntPtr)blockPtr, dest.Data, dest.Offset, ScalarCount);
}
/// <summary>
/// Copy raw 32bit floating point data to dest
/// </summary>
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++)
{
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)]
public void MultiplyAllInplace(Vector4 s)
{
V0L *= s;
V0R *= s;
V1L *= s;
V1R *= s;
V2L *= s;
V2R *= s;
V3L *= s;
V3R *= s;
V4L *= s;
V4R *= s;
V5L *= s;
V5R *= s;
V6L *= s;
V6R *= s;
V7L *= s;
V7R *= s;
this.V0L *= s;
this.V0R *= s;
this.V1L *= s;
this.V1R *= s;
this.V2L *= s;
this.V2R *= s;
this.V3L *= s;
this.V3R *= s;
this.V4L *= s;
this.V4R *= s;
this.V5L *= s;
this.V5R *= s;
this.V6L *= s;
this.V6R *= s;
this.V7L *= s;
this.V7R *= s;
}
// ReSharper disable once InconsistentNaming
public void IDCTInto(ref Block8x8F dest, ref Block8x8F temp)
/// <summary>
/// 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);
temp.iDCT2D8x4_LeftPart(ref dest);
temp.iDCT2D8x4_RightPart(ref dest);
this.TransposeInto(ref temp);
temp.IDCT8x4_LeftPart(ref dest);
temp.IDCT8x4_RightPart(ref dest);
dest.TransposeInto(ref temp);
temp.iDCT2D8x4_LeftPart(ref dest);
temp.iDCT2D8x4_RightPart(ref dest);
dest.MultiplyAllInplace(_0_125);
}
temp.IDCT8x4_LeftPart(ref dest);
temp.IDCT8x4_RightPart(ref dest);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void IDCTInplace()
{
Block8x8F result = new Block8x8F();
Block8x8F temp = new Block8x8F();
IDCTInto(ref result, ref temp);
this = result;
dest.MultiplyAllInplace(c_0_125);
}
private static readonly Vector4 _1_175876 = new Vector4(1.175876f);
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_899976 = new Vector4(-0.899976f);
private static readonly Vector4 _2_562915 = new Vector4(-2.562915f);
private static readonly Vector4 _0_298631 = new Vector4(0.298631f);
private static readonly Vector4 _2_053120 = new Vector4(2.053120f);
private static readonly Vector4 _3_072711 = new Vector4(3.072711f);
private static readonly Vector4 _1_501321 = new Vector4(1.501321f);
private static readonly Vector4 _0_541196 = new Vector4(0.541196f);
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_125 = new Vector4(0.1250f);
private static readonly Vector4 c_1_175876 = new Vector4(1.175876f);
private static readonly Vector4 c_1_961571 = new Vector4(-1.961571f);
private static readonly Vector4 c_0_390181 = new Vector4(-0.390181f);
private static readonly Vector4 c_0_899976 = new Vector4(-0.899976f);
private static readonly Vector4 c_2_562915 = new Vector4(-2.562915f);
private static readonly Vector4 c_0_298631 = new Vector4(0.298631f);
private static readonly Vector4 c_2_053120 = new Vector4(2.053120f);
private static readonly Vector4 c_3_072711 = new Vector4(3.072711f);
private static readonly Vector4 c_1_501321 = new Vector4(1.501321f);
private static readonly Vector4 c_0_541196 = new Vector4(0.541196f);
private static readonly Vector4 c_1_847759 = new Vector4(-1.847759f);
private static readonly Vector4 c_0_765367 = new Vector4(0.765367f);
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)]
internal void iDCT2D8x4_LeftPart(ref Block8x8F d)
internal void IDCT8x4_LeftPart(ref Block8x8F d)
{
/*
float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i;
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 my1 = this.V1L;
Vector4 my7 = this.V7L;
Vector4 mz0 = my1 + my7;
Vector4 my3 = V3L;
Vector4 my3 = this.V3L;
Vector4 mz2 = my3 + my7;
Vector4 my5 = V5L;
Vector4 my5 = this.V5L;
Vector4 mz1 = my3 + my5;
Vector4 mz3 = my1 + my5;
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];
//z4 = (z0 + z1) * r[3];
mz2 = mz2*_1_961571 + mz4;
mz3 = mz3*_0_390181 + mz4;
mz0 = mz0*_0_899976;
mz1 = mz1*_2_562915;
/*
-0.899976
-2.562915
-1.961571
-0.390181
z0 = z0 * (-r[3] + r[7]);
z1 = z1 * (-r[3] - r[1]);
z2 = z2 * (-r[3] - r[5]) + z4;
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;
Vector4 mz4 = ((mz0 + mz1) * c_1_175876);
mz2 = (mz2 * c_1_961571) + mz4;
mz3 = (mz3 * c_0_390181) + mz4;
mz0 = mz0 * c_0_899976;
mz1 = mz1 * c_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;
Vector4 mb0 = (my1 * c_1_501321) + mz0 + mz3;
Vector4 my2 = this.V2L;
Vector4 my6 = this.V6L;
mz4 = (my2 + my6) * c_0_541196;
Vector4 my0 = this.V0L;
Vector4 my4 = this.V4L;
mz0 = my0 + my4;
mz1 = my0 - my4;
mz2 = mz4 + my6*_1_847759;
mz3 = mz4 + my2*_0_765367;
mz2 = mz4 + (my6 * c_1_847759);
mz3 = mz4 + (my2 * c_0_765367);
my0 = mz0 + mz3;
my3 = mz0 - mz3;
my1 = 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.V7L = my0 - mb0;
@ -291,104 +235,53 @@ namespace ImageSharp.Formats
d.V5L = my2 - mb2;
d.V3L = 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)]
internal void iDCT2D8x4_RightPart(ref Block8x8F d)
internal void IDCT8x4_RightPart(ref Block8x8F d)
{
/*
float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i;
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 my1 = this.V1R;
Vector4 my7 = this.V7R;
Vector4 mz0 = my1 + my7;
Vector4 my3 = V3R;
Vector4 my3 = this.V3R;
Vector4 mz2 = my3 + my7;
Vector4 my5 = V5R;
Vector4 my5 = this.V5R;
Vector4 mz1 = my3 + my5;
Vector4 mz3 = my1 + my5;
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];
//z4 = (z0 + z1) * r[3];
mz2 = mz2*_1_961571 + mz4;
mz3 = mz3*_0_390181 + mz4;
mz0 = mz0*_0_899976;
mz1 = mz1*_2_562915;
/*
-0.899976
-2.562915
-1.961571
-0.390181
z0 = z0 * (-r[3] + r[7]);
z1 = z1 * (-r[3] - r[1]);
z2 = z2 * (-r[3] - r[5]) + z4;
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;
Vector4 mz4 = (mz0 + mz1) * c_1_175876;
mz2 = (mz2 * c_1_961571) + mz4;
mz3 = (mz3 * c_0_390181) + mz4;
mz0 = mz0 * c_0_899976;
mz1 = mz1 * c_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;
Vector4 mb0 = (my1 * c_1_501321) + mz0 + mz3;
Vector4 my2 = this.V2R;
Vector4 my6 = this.V6R;
mz4 = (my2 + my6) * c_0_541196;
Vector4 my0 = this.V0R;
Vector4 my4 = this.V4R;
mz0 = my0 + my4;
mz1 = my0 - my4;
mz2 = mz4 + my6*_1_847759;
mz3 = mz4 + my2*_0_765367;
mz2 = mz4 + (my6 * c_1_847759);
mz3 = mz4 + (my2 * c_0_765367);
my0 = mz0 + mz3;
my3 = mz0 - mz3;
my1 = 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.V7R = my0 - mb0;
@ -398,15 +291,7 @@ namespace ImageSharp.Formats
d.V5R = my2 - mb2;
d.V3R = 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]
{
@ -415,83 +300,94 @@ namespace ImageSharp.Formats
{
fixed (Block8x8F* p = &this)
{
float* fp = (float*) p;
float* fp = (float*)p;
return fp[idx];
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
fixed (Block8x8F* p = &this)
{
float* fp = (float*) p;
float* fp = (float*)p;
fp[idx] = value;
}
}
}
/// <summary>
/// Pointer-based "Indexer" (getter part)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx)
{
float* fp = (float*) blockPtr;
float* fp = (float*)blockPtr;
return fp[idx];
}
/// <summary>
/// Pointer-based "Indexer" (setter part)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value)
{
float* fp = (float*) blockPtr;
float* fp = (float*)blockPtr;
fp[idx] = value;
}
/// <summary>
/// Fill the block with defaults (zeroes)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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)
{
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)
{
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>
/// 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe void CopyColorsTo(
MutableSpan<byte> buffer,
int stride,
Block8x8F* temp)
internal unsafe void CopyColorsTo(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++)
{
buffer[0] = (byte) src[0];
buffer[1] = (byte) src[1];
buffer[2] = (byte) src[2];
buffer[3] = (byte) src[3];
buffer[4] = (byte) src[4];
buffer[5] = (byte) src[5];
buffer[6] = (byte) src[6];
buffer[7] = (byte) src[7];
buffer[0] = (byte)src[0];
buffer[1] = (byte)src[1];
buffer[2] = (byte)src[2];
buffer[3] = (byte)src[3];
buffer[4] = (byte)src[4];
buffer[5] = (byte)src[5];
buffer[6] = (byte)src[6];
buffer[7] = (byte)src[7];
buffer.AddOffset(stride);
src += 8;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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.
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
namespace ImageSharp.Formats
{
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
/// <summary>
/// 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.
@ -17,25 +16,14 @@ namespace ImageSharp.Formats
/// </summary>
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);
/// <summary>
/// Creates a new instance of the <see cref="Bytes"/>, and initializes it's buffer.
/// </summary>
public static Bytes Create()
{
return new Bytes
{
Buffer = ArrayPool.Rent(4096)
};
return new Bytes { Buffer = ArrayPool.Rent(4096) };
}
/// <summary>
@ -57,15 +45,14 @@ namespace ImageSharp.Formats
public void Dispose()
{
if (Buffer!= null) ArrayPool.Return(Buffer);
Buffer = null;
if (this.Buffer != null) ArrayPool.Return(this.Buffer);
this.Buffer = null;
}
/// <summary>
/// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data.
/// </summary>
/// <returns>The <see cref="byte"/></returns>
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByteStuffedByte(Stream inputStream, out JpegDecoderCore.ErrorCodes errorCode)
{
byte x;
@ -87,7 +74,8 @@ namespace ImageSharp.Formats
{
errorCode = JpegDecoderCore.ErrorCodes.MissingFF00;
return 0;
//throw new MissingFF00Exception();
// throw new MissingFF00Exception();
}
this.I++;
@ -110,7 +98,8 @@ namespace ImageSharp.Formats
{
errorCode = JpegDecoderCore.ErrorCodes.MissingFF00;
return 0;
//throw new MissingFF00Exception();
// throw new MissingFF00Exception();
}
return JpegConstants.Markers.XFF;
@ -165,6 +154,5 @@ namespace ImageSharp.Formats
this.J += n;
}
}
}
}

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

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

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

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

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

@ -3,14 +3,12 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ImageSharp.Formats
{
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
/// <summary>
@ -71,12 +69,12 @@ namespace ImageSharp.Formats
/// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
/// </summary>
private static readonly int[] Unzig =
{
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26,
33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57,
50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31,
39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
};
{
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33,
40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50,
43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63,
};
/// <summary>
/// The component array
@ -92,7 +90,6 @@ namespace ImageSharp.Formats
/// The huffman trees
/// </summary>
//private readonly Huffman[,] huffmanTrees;
private readonly Huffman[] huffmanTrees;
/// <summary>
@ -223,7 +220,6 @@ namespace ImageSharp.Formats
}
}
/// <summary>
/// Decodes the image from the specified this._stream and sets
/// the data to image.
@ -234,8 +230,7 @@ namespace ImageSharp.Formats
/// <param name="stream">The stream, where the image should be.</param>
/// <param name="configOnly">Whether to decode metadata only.</param>
public void Decode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream, bool configOnly)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
{
this.inputStream = stream;
@ -373,7 +368,8 @@ namespace ImageSharp.Formats
this.ProcessApp14Marker(remaining);
break;
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);
}
@ -422,7 +418,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Missing SOS marker.");
}
}
/// <summary>
/// Processes a Define Huffman Table marker, and initializes a huffman
/// struct from its contents. Specified in section B.2.4.2.
@ -451,13 +447,12 @@ namespace ImageSharp.Formats
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)
{
// Read nCodes and huffman.Valuess (and derive h.Length).
// nCodes[i] is the number of codes with code length i.
// h.Length is the total number of codes.
@ -552,7 +547,7 @@ namespace ImageSharp.Formats
private byte DecodeHuffman(ref Huffman huffman)
{
// Copy stuff to the stack:
if (huffman.Length == 0)
{
throw new ImageFormatException("Uninitialized Huffman table");
@ -560,35 +555,24 @@ namespace ImageSharp.Formats
if (this.bits.UnreadBits < 8)
{
//try
//{
var errorCode = bits.EnsureNBits(8, this);
var errorCode = this.bits.EnsureNBits(8, this);
if (errorCode == ErrorCodes.NoError)
{
ushort v = huffman.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff];
if (errorCode == ErrorCodes.NoError)
{
ushort v = huffman.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff];
if (v != 0)
{
byte n = (byte)((v & 0xff) - 1);
this.bits.UnreadBits -= n;
this.bits.Mask >>= n;
return (byte)(v >> 8);
}
}
else
if (v != 0)
{
this.UnreadByteStuffedByte();
byte n = (byte)((v & 0xff) - 1);
this.bits.UnreadBits -= n;
this.bits.Mask >>= n;
return (byte)(v >> 8);
}
//}
//catch (ShortHuffmanDataException) // TODO: This is actually never thrown!
//{
// if (this.bytes.UnreadableBytes != 0)
// {
// this.UnreadByteStuffedByte();
// }
//}
}
else
{
this.UnreadByteStuffedByte();
}
}
int code = 0;
@ -666,37 +650,6 @@ namespace ImageSharp.Formats
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>
/// Undoes the most recent ReadByteStuffedByte call,
/// giving a byte of data back from bits to bytes. The Huffman look-up table
@ -716,7 +669,6 @@ namespace ImageSharp.Formats
}
}
/// <summary>
/// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
/// </summary>
@ -724,74 +676,9 @@ namespace ImageSharp.Formats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByte()
{
return bytes.ReadByte(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;
return this.bytes.ReadByte(this.inputStream);
}
/// <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>
/// Reads exactly length bytes into data. It does not care about byte stuffing.
/// </summary>
@ -805,7 +692,7 @@ namespace ImageSharp.Formats
{
if (this.bits.UnreadBits >= 8)
{
UnreadByteStuffedByte();
this.UnreadByteStuffedByte();
}
this.bytes.UnreadableBytes = 0;
@ -826,7 +713,7 @@ namespace ImageSharp.Formats
length -= 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;
}
this.bytes.Fill(inputStream);
this.bytes.Fill(this.inputStream);
}
}
@ -988,7 +875,8 @@ namespace ImageSharp.Formats
case 1:
{
// 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");
}
@ -999,7 +887,8 @@ namespace ImageSharp.Formats
case 2:
{
// 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");
}
@ -1039,7 +928,8 @@ namespace ImageSharp.Formats
break;
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");
}
@ -1157,11 +1047,8 @@ namespace ImageSharp.Formats
remaining -= 13;
// TODO: We should be using constants for this.
this.isJfif = this.temp[0] == 'J' &&
this.temp[1] == 'F' &&
this.temp[2] == 'I' &&
this.temp[3] == 'F' &&
this.temp[4] == '\x00';
this.isJfif = this.temp[0] == 'J' && this.temp[1] == 'F' && this.temp[2] == 'I' && this.temp[3] == 'F'
&& this.temp[4] == '\x00';
if (this.isJfif)
{
@ -1183,8 +1070,7 @@ namespace ImageSharp.Formats
/// <param name="remaining">The remaining bytes in the segment block.</param>
/// <param name="image">The image.</param>
private void ProcessApp1Marker<TColor, TPacked>(int remaining, Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
{
if (remaining < 6)
{
@ -1195,12 +1081,8 @@ namespace ImageSharp.Formats
byte[] profile = new byte[remaining];
this.ReadFull(profile, 0, remaining);
if (profile[0] == 'E' &&
profile[1] == 'x' &&
profile[2] == 'i' &&
profile[3] == 'f' &&
profile[4] == '\0' &&
profile[5] == '\0')
if (profile[0] == 'E' && profile[1] == 'x' && profile[2] == 'i' && profile[3] == 'f' && profile[4] == '\0'
&& profile[5] == '\0')
{
image.ExifProfile = new ExifProfile(profile);
}
@ -1223,11 +1105,8 @@ namespace ImageSharp.Formats
this.ReadFull(this.temp, 0, 12);
remaining -= 12;
if (this.temp[0] == 'A' &&
this.temp[1] == 'd' &&
this.temp[2] == 'o' &&
this.temp[3] == 'b' &&
this.temp[4] == 'e')
if (this.temp[0] == 'A' && this.temp[1] == 'd' && this.temp[2] == 'o' && this.temp[3] == 'b'
&& this.temp[4] == 'e')
{
this.adobeTransformValid = true;
this.adobeTransform = this.temp[11];
@ -1248,12 +1127,12 @@ namespace ImageSharp.Formats
/// <param name="height">The image height.</param>
/// <param name="image">The image.</param>
private void ConvertFromCmyk<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
{
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
@ -1274,21 +1153,21 @@ namespace ImageSharp.Formats
0,
height,
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];
byte cb = this.ycbcrImage.CbChannel[co + (x / scale)];
byte cr = this.ycbcrImage.CrChannel[co + (x / scale)];
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
TColor packed = default(TColor);
this.PackCmyk<TColor, TPacked>(ref packed, yy, cb, cr, x, y);
pixels[x, y] = packed;
}
});
for (int x = 0; x < width; x++)
{
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);
@ -1304,8 +1183,7 @@ namespace ImageSharp.Formats
/// <param name="height">The image height.</param>
/// <param name="image">The image.</param>
private void ConvertFromGrayScale<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
{
image.InitPixels(width, height);
@ -1316,17 +1194,17 @@ namespace ImageSharp.Formats
height,
Bootstrapper.Instance.ParallelOptions,
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);
packed.PackFromBytes(rgb, rgb, rgb, 255);
pixels[x, y] = packed;
}
});
TColor packed = default(TColor);
packed.PackFromBytes(rgb, rgb, rgb, 255);
pixels[x, y] = packed;
}
});
}
this.AssignResolution(image);
@ -1341,8 +1219,7 @@ namespace ImageSharp.Formats
/// <param name="height">The image height.</param>
/// <param name="image">The image.</param>
private void ConvertFromYCbCr<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
{
int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor;
image.InitPixels(width, height);
@ -1354,27 +1231,26 @@ namespace ImageSharp.Formats
height,
Bootstrapper.Instance.ParallelOptions,
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];
byte cb = this.ycbcrImage.CbChannel[co + (x / scale)];
byte cr = this.ycbcrImage.CrChannel[co + (x / scale)];
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
TColor packed = default(TColor);
PackYcbCr<TColor, TPacked>(ref packed, yy, cb, cr);
pixels[x, y] = packed;
}
});
for (int x = 0; x < width; x++)
{
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);
}
/// <summary>
/// Converts the image from the original RBG image pixels.
/// </summary>
@ -1384,8 +1260,7 @@ namespace ImageSharp.Formats
/// <param name="height">The height.</param>
/// <param name="image">The image.</param>
private void ConvertFromRGB<TColor, TPacked>(int width, int height, Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
{
int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor;
image.InitPixels(width, height);
@ -1397,21 +1272,21 @@ namespace ImageSharp.Formats
height,
Bootstrapper.Instance.ParallelOptions,
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];
byte green = this.ycbcrImage.CbChannel[co + (x / scale)];
byte blue = this.ycbcrImage.CrChannel[co + (x / scale)];
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
TColor packed = default(TColor);
packed.PackFromBytes(red, green, blue, 255);
pixels[x, y] = packed;
}
});
for (int x = 0; x < width; x++)
{
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);
@ -1424,8 +1299,7 @@ namespace ImageSharp.Formats
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="image">The image to assign the resolution to.</param>
private void AssignResolution<TColor, TPacked>(Image<TColor, TPacked> image)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
{
if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0)
{
@ -1476,7 +1350,7 @@ namespace ImageSharp.Formats
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
@ -1546,7 +1420,8 @@ namespace ImageSharp.Formats
int compIndex = scan[i].Index;
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];
}
@ -1573,7 +1448,7 @@ namespace ImageSharp.Formats
// Tricky way to copy contents of the Unzig static variable to the stack:
StackallocUnzigData unzigOnStack = new StackallocUnzigData();
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++)
{
@ -1585,7 +1460,6 @@ namespace ImageSharp.Formats
int hi = this.componentArray[compIndex].HorizontalFactor;
int vi = this.componentArray[compIndex].VerticalFactor;
for (int j = 0; j < hi * vi; j++)
{
// 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;
// TODO: Find a way to clean up this mess
fixed (Block8x8F* qtp = &this.quantizationTables[qtIndex])
{
if (this.isProgressive)
// 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,
&temp1,
&temp2,
unzigPtr,
scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi, bx,
qtp
);
scan,
i,
zigStart,
zigEnd,
al,
dc,
compIndex,
@by,
mxx,
hi,
bx,
qtp);
}
}
else
{
b.Clear();
ProcessBlockImpl(ah,
this.ProcessBlockImpl(
ah,
&b,
&temp1,
&temp2,
unzigPtr,
scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi,
bx, qtp
);
scan,
i,
zigStart,
zigEnd,
al,
dc,
compIndex,
@by,
mxx,
hi,
bx,
qtp);
}
}
}
// for j
@ -1709,14 +1599,22 @@ namespace ImageSharp.Formats
}
private void ProcessBlockImpl(
int ah,
Block8x8F* b,
int ah,
Block8x8F* b,
Block8x8F* temp1,
Block8x8F* temp2,
int* unzigPtr,
Scan[] scan,
int i, int zigStart, int zigEnd, int al,
int[] dc, int compIndex, int @by, int mxx, int hi, int bx,
Scan[] scan,
int i,
int zigStart,
int zigEnd,
int al,
int[] dc,
int compIndex,
int @by,
int mxx,
int hi,
int bx,
Block8x8F* qt)
{
var huffmannIdx = AcTable * ThRowSize + scan[i].AcTableSelector;
@ -1732,7 +1630,8 @@ namespace ImageSharp.Formats
zig++;
// 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)
{
throw new ImageFormatException("Excessive DC component");
@ -1741,7 +1640,7 @@ namespace ImageSharp.Formats
int deltaDC = this.bits.ReceiveExtend(value, this);
dc[compIndex] += deltaDC;
//b[0] = dc[compIndex] << al;
// b[0] = dc[compIndex] << al;
Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al);
}
@ -1752,7 +1651,7 @@ namespace ImageSharp.Formats
else
{
// Decode the AC coefficients, as specified in section F.2.2.2.
//Huffman huffv = ;
// Huffman huffv = ;
for (; zig <= zigEnd; zig++)
{
byte value = this.DecodeHuffman(ref this.huffmanTrees[huffmannIdx]);
@ -1768,7 +1667,7 @@ namespace ImageSharp.Formats
int ac = this.bits.ReceiveExtend(val1, this);
//b[Unzig[zig]] = ac << al;
// b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al);
}
else
@ -1813,8 +1712,8 @@ namespace ImageSharp.Formats
// Dequantize, perform the inverse DCT and store the block to the image.
Block8x8F.UnZig(b, qt, unzigPtr);
b->IDCTInto(ref *temp1, ref *temp2);
b->TransformIDCTInto(ref *temp1, ref *temp2);
byte[] dst;
int offset;
@ -1861,10 +1760,10 @@ namespace ImageSharp.Formats
}
// Level shift by +128, clip to [0, 255], and write to dst.
temp1->CopyColorsTo(new MutableSpan<byte>(dst, offset), stride, temp2);
}
private void ProcessScanImpl(int i, ref Scan currentScan, Scan[] scan, ref int totalHv)
{
// Component selector.
@ -1886,10 +1785,15 @@ namespace ImageSharp.Formats
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
// 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;
currentScan.DcTableSelector = (byte)(this.temp[2 + (2 * i)] >> 4);
@ -1925,6 +1828,7 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="b">The block of coefficients</param>
/// <param name="h">The Huffman tree</param>
/// <param name="unzigPtr"></param>
/// <param name="zigStart">The zig-zag start index</param>
/// <param name="zigEnd">The zig-zag end index</param>
/// <param name="delta">The low transform offset</param>
@ -1941,11 +1845,11 @@ namespace ImageSharp.Formats
bool bit = this.DecodeBit();
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;
//b[0] = stuff;
// b[0] = stuff;
Block8x8F.SetScalarAt(b, 0, stuff);
}
@ -2145,7 +2049,8 @@ namespace ImageSharp.Formats
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>
@ -2160,8 +2065,7 @@ namespace ImageSharp.Formats
/// <param name="cr">The cr chroma component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void PackYcbCr<TColor, TPacked>(ref TColor packed, byte y, byte cb, byte cr)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
{
int ccb = cb - 128;
int ccr = cr - 128;
@ -2186,8 +2090,7 @@ namespace ImageSharp.Formats
/// <param name="xx">The x-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)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
where TColor : struct, IPackedPixel<TPacked> where TPacked : struct
{
// TODO: We can speed this up further with Vector4
int ccb = cb - 128;
@ -2265,11 +2168,11 @@ namespace ImageSharp.Formats
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()
{
float sum = 0;
Measure(Times, () =>
this.Measure(Times, () =>
{
Block8x8F block = new Block8x8F();
@ -44,8 +44,8 @@ namespace ImageSharp.Tests.Formats.Jpg
{
sum += block[i];
}
});
Assert.Equal(sum, 64f*63f*0.5f);
});
Assert.Equal(sum, 64f * 63f * 0.5f);
}
[Fact]
@ -151,28 +151,12 @@ namespace ImageSharp.Tests.Formats.Jpg
v.CopyTo(mirror);
});
Assert.Equal(data, 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]
public void TransposeInto()
{
@ -231,7 +215,7 @@ namespace ImageSharp.Tests.Formats.Jpg
Block8x8F dest = new Block8x8F();
source.iDCT2D8x4_LeftPart(ref dest);
source.IDCT8x4_LeftPart(ref dest);
float[] actualDestArray = new float[64];
dest.CopyTo(actualDestArray);
@ -256,7 +240,7 @@ namespace ImageSharp.Tests.Formats.Jpg
Block8x8F dest = new Block8x8F();
source.iDCT2D8x4_RightPart(ref dest);
source.IDCT8x4_RightPart(ref dest);
float[] actualDestArray = new float[64];
dest.CopyTo(actualDestArray);
@ -302,7 +286,7 @@ namespace ImageSharp.Tests.Formats.Jpg
Block8x8F dest = new Block8x8F();
Block8x8F tempBuffer = new Block8x8F();
source.IDCTInto(ref dest, ref tempBuffer);
source.TransformIDCTInto(ref dest, ref tempBuffer);
float[] actualDestArray = new float[64];
dest.CopyTo(actualDestArray);
@ -342,26 +326,7 @@ namespace ImageSharp.Tests.Formats.Jpg
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()
{
float[] result = new float[64];
@ -376,7 +341,7 @@ namespace ImageSharp.Tests.Formats.Jpg
}
[Fact]
public void ColorifyInto()
public void TransformByteConvetibleColorValuesInto()
{
Block8x8F block = new Block8x8F();
var input = Create8x8ColorCropTestData();
@ -386,7 +351,7 @@ namespace ImageSharp.Tests.Formats.Jpg
Block8x8F dest = new Block8x8F();
block.ColorifyInto(ref dest);
block.TransformByteConvetibleColorValuesInto(ref dest);
float[] array = new float[64];
dest.CopyTo(array);

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

@ -1,46 +1,57 @@
using System;
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using ImageSharp.Formats;
// ReSharper disable InconsistentNaming
// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests.Formats.Jpg
{
using System.Numerics;
using System.Runtime.CompilerServices;
using ImageSharp.Formats;
/// <summary>
/// This class contains simplified (unefficient) reference implementations to produce verification data for unit tests
/// DCT code Ported from https://github.com/norishigefukushima/dct_simd
/// </summary>
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)
{
for (int i = 1; i < 8; i++)
{
int i8 = i*8;
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;
data[i8 + j] = data[j * 8 + i];
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)
{
for (int i = 0; i < 8; i++)
{
int i8 = i*8;
int i8 = i * 8;
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 z0, z1, z2, z3, z4;
@ -58,23 +69,23 @@ namespace ImageSharp.Tests.Formats.Jpg
z1 = y[3] + y[5];
z2 = y[3] + y[7];
z3 = y[1] + y[5];
z4 = (z0 + z1)*r3;
z4 = (z0 + z1) * r3;
z0 = z0*(-r3 + r7);
z1 = z1*(-r3 - r1);
z2 = z2*(-r3 - r5) + z4;
z3 = z3*(-r3 + r5) + z4;
z0 = z0 * (-r3 + r7);
z1 = z1 * (-r3 - r1);
z2 = z2 * (-r3 - r5) + z4;
z3 = z3 * (-r3 + r5) + z4;
b3 = y[7]*(-r1 + r3 + r5 - r7) + z0 + z2;
b2 = y[5]*(r1 + r3 - r5 + r7) + z1 + z3;
b1 = y[3]*(r1 + r3 + r5 - r7) + z1 + z2;
b0 = y[1]*(r1 + r3 - r5 - r7) + z0 + z3;
b3 = y[7] * (-r1 + r3 + r5 - r7) + z0 + z2;
b2 = y[5] * (r1 + r3 - r5 + r7) + z1 + z3;
b1 = y[3] * (r1 + r3 + r5 - r7) + z1 + z2;
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];
z1 = y[0] - y[4];
z2 = z4 - y[6]*(r2 + r6);
z3 = z4 + y[2]*(r2 - r6);
z2 = z4 - y[6] * (r2 + r6);
z3 = z4 + y[2] * (r2 - r6);
a0 = z0 + z3;
a3 = z0 - z3;
a1 = z1 + z2;
@ -90,20 +101,27 @@ namespace ImageSharp.Tests.Formats.Jpg
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)
{
int 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);
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);
@ -113,8 +131,6 @@ namespace ImageSharp.Tests.Formats.Jpg
d[j] *= 0.125f;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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]);
}
[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)]
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;
}
[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_961571 = new Vector4(-1.961571f);
private static readonly Vector4 _0_390181 = new Vector4(-0.390181f);
private static readonly Vector4 _0_899976 = new Vector4(-0.899976f);
private static readonly Vector4 _2_562915 = new Vector4(-2.562915f);
private static readonly Vector4 _0_298631 = new Vector4(0.298631f);
private static readonly Vector4 _2_053120 = new Vector4(2.053120f);
private static readonly Vector4 _3_072711 = new Vector4(3.072711f);
private static readonly Vector4 _1_501321 = new Vector4(1.501321f);
private static readonly Vector4 _0_541196 = new Vector4(0.541196f);
private static readonly Vector4 _1_847759 = new Vector4(-1.847759f);
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)
{
/*
@ -178,7 +196,7 @@ namespace ImageSharp.Tests.Formats.Jpg
6:
7: 0.275899
*/
Vector4 my1 = _mm_load_ps(y, 8);
Vector4 my7 = _mm_load_ps(y, 56);
Vector4 mz0 = my1 + my7;
@ -188,16 +206,16 @@ namespace ImageSharp.Tests.Formats.Jpg
Vector4 my5 = _mm_load_ps(y, 40);
Vector4 mz1 = my3 + 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];
//z4 = (z0 + z1) * r[3];
mz2 = mz2* _1_961571 + mz4;
mz3 = mz3* _0_390181 + mz4;
mz0 = mz0* _0_899976;
mz1 = mz1* _2_562915;
mz2 = mz2 * _1_961571 + mz4;
mz3 = mz3 * _0_390181 + mz4;
mz0 = mz0 * _0_899976;
mz1 = mz1 * _2_562915;
/*
-0.899976
-2.562915
@ -208,11 +226,10 @@ namespace ImageSharp.Tests.Formats.Jpg
z2 = z2 * (-r[3] - r[5]) + z4;
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;
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
@ -227,14 +244,14 @@ namespace ImageSharp.Tests.Formats.Jpg
Vector4 my2 = _mm_load_ps(y, 16);
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 my4 = _mm_load_ps(y, 32);
mz0 = my0 + my4;
mz1 = my0 - my4;
mz2 = mz4 + my6* _1_847759;
mz3 = mz4 + my2* _0_765367;
mz2 = mz4 + my6 * _1_847759;
mz3 = mz4 + my2 * _0_765367;
my0 = 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)
{
Transpose8x8(s, temp);
iDCT2D8x4_32f(temp, d);
iDCT2D8x4_32f(temp.Slice(4), d.Slice(4));
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
}
/// <summary>
/// Copies color values from block to the destination image buffer.
/// </summary>
/// <param name="block"></param>
/// <param name="buffer"></param>
/// <param name="stride"></param>
internal static unsafe void CopyColorsTo(ref Block8x8F block, MutableSpan<byte> buffer, int stride)
{
fixed (Block8x8F* p = &block)
@ -356,9 +330,6 @@ namespace ImageSharp.Tests.Formats.Jpg
}
}
}
}
}
}
Loading…
Cancel
Save