diff --git a/src/ImageSharp/Formats/Jpg/Components/Block.cs b/src/ImageSharp/Formats/Jpg/Components/Block.cs
deleted file mode 100644
index 0531153cd..000000000
--- a/src/ImageSharp/Formats/Jpg/Components/Block.cs
+++ /dev/null
@@ -1,272 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp.Formats
-{
- using System;
- using System.Buffers;
- using System.Runtime.CompilerServices;
-
- ///
- /// Represents an 8x8 block of coefficients to transform and encode.
- ///
- internal struct Block : IDisposable
- {
- ///
- /// Gets the size of the block.
- ///
- public const int BlockSize = 64;
-
- ///
- /// Gets the array of block data.
- ///
- public int[] Data;
-
- ///
- /// A pool of reusable buffers.
- ///
- private static readonly ArrayPool ArrayPool = ArrayPool.Create(BlockSize, 50);
-
- ///
- /// Gets a value indicating whether the block is initialized
- ///
- public bool IsInitialized => this.Data != null;
-
- ///
- /// Gets the pixel data at the given block index.
- ///
- /// The index of the data to return.
- ///
- /// The .
- ///
- public int this[int index]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- return this.Data[index];
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- set
- {
- this.Data[index] = value;
- }
- }
-
- ///
- /// Creates a new block
- ///
- /// The
- public static Block Create()
- {
- Block block = default(Block);
- block.Init();
- return block;
- }
-
- ///
- /// Returns an array of blocks of the given length.
- ///
- /// The number to create.
- /// The
- public static Block[] CreateArray(int count)
- {
- Block[] result = new Block[count];
- for (int i = 0; i < result.Length; i++)
- {
- result[i].Init();
- }
-
- return result;
- }
-
- ///
- /// Disposes of the collection of blocks
- ///
- /// The blocks.
- public static void DisposeAll(Block[] blocks)
- {
- for (int i = 0; i < blocks.Length; i++)
- {
- blocks[i].Dispose();
- }
- }
-
- ///
- /// Initializes the new block.
- ///
- public void Init()
- {
- this.Data = ArrayPool.Rent(BlockSize);
- }
-
- ///
- public void Dispose()
- {
- // TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement!
- if (this.Data != null)
- {
- ArrayPool.Return(this.Data, true);
- this.Data = null;
- }
- }
-
- ///
- /// Clears the block data
- ///
- public void Clear()
- {
- for (int i = 0; i < this.Data.Length; i++)
- {
- this.Data[i] = 0;
- }
- }
-
- ///
- /// Clones the current block
- ///
- /// The
- public Block Clone()
- {
- Block clone = Create();
- Array.Copy(this.Data, clone.Data, BlockSize);
- return clone;
- }
- }
-
- ///
- /// TODO: Should be removed, when JpegEncoderCore is refactored to use Block8x8F
- /// Temporal class to make refactoring easier.
- /// 1. Refactor Block -> BlockF
- /// 2. Test
- /// 3. Refactor BlockF -> Block8x8F
- ///
- internal struct BlockF : IDisposable
- {
- ///
- /// Size of the block.
- ///
- public const int BlockSize = 64;
-
- ///
- /// The array of block data.
- ///
- public float[] Data;
-
- ///
- /// A pool of reusable buffers.
- ///
- private static readonly ArrayPool ArrayPool = ArrayPool.Create(BlockSize, 50);
-
- ///
- /// Gets a value indicating whether the block is initialized
- ///
- public bool IsInitialized => this.Data != null;
-
- ///
- /// Gets the pixel data at the given block index.
- ///
- /// The index of the data to return.
- ///
- /// The .
- ///
- public float this[int index]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- return this.Data[index];
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- set
- {
- this.Data[index] = value;
- }
- }
-
- ///
- /// Creates a new block
- ///
- /// The
- public static BlockF Create()
- {
- var block = default(BlockF);
- block.Init();
- return block;
- }
-
- ///
- /// Returns an array of blocks of the given length.
- ///
- /// The number to create.
- /// The
- public static BlockF[] CreateArray(int count)
- {
- BlockF[] result = new BlockF[count];
- for (int i = 0; i < result.Length; i++)
- {
- result[i].Init();
- }
-
- return result;
- }
-
- ///
- /// Disposes of the collection of blocks
- ///
- /// The blocks.
- public static void DisposeAll(BlockF[] blocks)
- {
- for (int i = 0; i < blocks.Length; i++)
- {
- blocks[i].Dispose();
- }
- }
-
- ///
- /// Clears the block data
- ///
- public void Clear()
- {
- for (int i = 0; i < this.Data.Length; i++)
- {
- this.Data[i] = 0;
- }
- }
-
- ///
- /// Clones the current block
- ///
- /// The
- public BlockF Clone()
- {
- BlockF clone = Create();
- Array.Copy(this.Data, clone.Data, BlockSize);
- return clone;
- }
-
- ///
- /// Initializes the new block.
- ///
- public void Init()
- {
- // this.Data = new int[BlockSize];
- this.Data = ArrayPool.Rent(BlockSize);
- }
-
- ///
- public void Dispose()
- {
- // TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement!
- if (this.Data != null)
- {
- ArrayPool.Return(this.Data, true);
- this.Data = null;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
index 89b838289..66ef146e4 100644
--- a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
@@ -229,7 +229,7 @@ namespace ImageSharp.Formats
{
float* b = (float*)blockPtr;
float* qtp = (float*)qtPtr;
- for (int zig = 0; zig < BlockF.BlockSize; zig++)
+ for (int zig = 0; zig < ScalarCount; zig++)
{
float* unzigPos = b + unzigPtr[zig];
float val = *unzigPos;
@@ -280,24 +280,6 @@ namespace ImageSharp.Formats
this = default(Block8x8F);
}
- ///
- /// TODO: Should be removed when BlockF goes away
- ///
- /// Legacy block
- public void LoadFrom(ref BlockF legacyBlock)
- {
- this.LoadFrom(legacyBlock.Data);
- }
-
- ///
- /// TODO: Should be removed when BlockF goes away
- ///
- /// Legacy block
- public void CopyTo(ref BlockF legacyBlock)
- {
- this.CopyTo(legacyBlock.Data);
- }
-
///
/// Level shift by +128, clip to [0, 255], and write to buffer.
///
diff --git a/src/ImageSharp/Formats/Jpg/Components/FDCT.cs b/src/ImageSharp/Formats/Jpg/Components/FDCT.cs
deleted file mode 100644
index 650656ab2..000000000
--- a/src/ImageSharp/Formats/Jpg/Components/FDCT.cs
+++ /dev/null
@@ -1,162 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp.Formats
-{
- ///
- /// Performs a fast, forward discrete cosine transform against the given block
- /// decomposing it into 64 orthogonal basis signals.
- ///
- internal class FDCT
- {
- // Trigonometric constants in 13-bit fixed point format.
- // TODO: Rename and describe these.
-#pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore
- private const int Fix_0_298631336 = 2446;
- private const int Fix_0_390180644 = 3196;
- private const int Fix_0_541196100 = 4433;
- private const int Fix_0_765366865 = 6270;
- private const int Fix_0_899976223 = 7373;
- private const int Fix_1_175875602 = 9633;
- private const int Fix_1_501321110 = 12299;
- private const int Fix_1_847759065 = 15137;
- private const int Fix_1_961570560 = 16069;
- private const int Fix_2_053119869 = 16819;
- private const int Fix_2_562915447 = 20995;
- private const int Fix_3_072711026 = 25172;
-#pragma warning restore SA1310 // FieldNamesMustNotContainUnderscore
-
- ///
- /// The number of bits
- ///
- private const int Bits = 13;
-
- ///
- /// The number of bits to shift by on the first pass.
- ///
- private const int Pass1Bits = 2;
-
- ///
- /// The value to shift by
- ///
- private const int CenterJSample = 128;
-
- ///
- /// Performs a forward DCT on an 8x8 block of coefficients, including a level shift.
- ///
- /// The block of coefficients.
- public static void Transform(ref Block block)
- {
- // Pass 1: process rows.
- for (int y = 0; y < 8; y++)
- {
- int y8 = y * 8;
-
- int x0 = block[y8];
- int x1 = block[y8 + 1];
- int x2 = block[y8 + 2];
- int x3 = block[y8 + 3];
- int x4 = block[y8 + 4];
- int x5 = block[y8 + 5];
- int x6 = block[y8 + 6];
- int x7 = block[y8 + 7];
-
- int tmp0 = x0 + x7;
- int tmp1 = x1 + x6;
- int tmp2 = x2 + x5;
- int tmp3 = x3 + x4;
-
- int tmp10 = tmp0 + tmp3;
- int tmp12 = tmp0 - tmp3;
- int tmp11 = tmp1 + tmp2;
- int tmp13 = tmp1 - tmp2;
-
- tmp0 = x0 - x7;
- tmp1 = x1 - x6;
- tmp2 = x2 - x5;
- tmp3 = x3 - x4;
-
- block[y8] = (tmp10 + tmp11 - (8 * CenterJSample)) << Pass1Bits;
- block[y8 + 4] = (tmp10 - tmp11) << Pass1Bits;
- int z1 = (tmp12 + tmp13) * Fix_0_541196100;
- z1 += 1 << (Bits - Pass1Bits - 1);
- block[y8 + 2] = (z1 + (tmp12 * Fix_0_765366865)) >> (Bits - Pass1Bits);
- block[y8 + 6] = (z1 - (tmp13 * Fix_1_847759065)) >> (Bits - Pass1Bits);
-
- tmp10 = tmp0 + tmp3;
- tmp11 = tmp1 + tmp2;
- tmp12 = tmp0 + tmp2;
- tmp13 = tmp1 + tmp3;
- z1 = (tmp12 + tmp13) * Fix_1_175875602;
- z1 += 1 << (Bits - Pass1Bits - 1);
- tmp0 = tmp0 * Fix_1_501321110;
- tmp1 = tmp1 * Fix_3_072711026;
- tmp2 = tmp2 * Fix_2_053119869;
- tmp3 = tmp3 * Fix_0_298631336;
- tmp10 = tmp10 * -Fix_0_899976223;
- tmp11 = tmp11 * -Fix_2_562915447;
- tmp12 = tmp12 * -Fix_0_390180644;
- tmp13 = tmp13 * -Fix_1_961570560;
-
- tmp12 += z1;
- tmp13 += z1;
- block[y8 + 1] = (tmp0 + tmp10 + tmp12) >> (Bits - Pass1Bits);
- block[y8 + 3] = (tmp1 + tmp11 + tmp13) >> (Bits - Pass1Bits);
- block[y8 + 5] = (tmp2 + tmp11 + tmp12) >> (Bits - Pass1Bits);
- block[y8 + 7] = (tmp3 + tmp10 + tmp13) >> (Bits - Pass1Bits);
- }
-
- // Pass 2: process columns.
- // We remove pass1Bits scaling, but leave results scaled up by an overall factor of 8.
- for (int x = 0; x < 8; x++)
- {
- int tmp0 = block[x] + block[56 + x];
- int tmp1 = block[8 + x] + block[48 + x];
- int tmp2 = block[16 + x] + block[40 + x];
- int tmp3 = block[24 + x] + block[32 + x];
-
- int tmp10 = tmp0 + tmp3 + (1 << (Pass1Bits - 1));
- int tmp12 = tmp0 - tmp3;
- int tmp11 = tmp1 + tmp2;
- int tmp13 = tmp1 - tmp2;
-
- tmp0 = block[x] - block[56 + x];
- tmp1 = block[8 + x] - block[48 + x];
- tmp2 = block[16 + x] - block[40 + x];
- tmp3 = block[24 + x] - block[32 + x];
-
- block[x] = (tmp10 + tmp11) >> Pass1Bits;
- block[32 + x] = (tmp10 - tmp11) >> Pass1Bits;
-
- int z1 = (tmp12 + tmp13) * Fix_0_541196100;
- z1 += 1 << (Bits + Pass1Bits - 1);
- block[16 + x] = (z1 + (tmp12 * Fix_0_765366865)) >> (Bits + Pass1Bits);
- block[48 + x] = (z1 - (tmp13 * Fix_1_847759065)) >> (Bits + Pass1Bits);
-
- tmp10 = tmp0 + tmp3;
- tmp11 = tmp1 + tmp2;
- tmp12 = tmp0 + tmp2;
- tmp13 = tmp1 + tmp3;
- z1 = (tmp12 + tmp13) * Fix_1_175875602;
- z1 += 1 << (Bits + Pass1Bits - 1);
- tmp0 = tmp0 * Fix_1_501321110;
- tmp1 = tmp1 * Fix_3_072711026;
- tmp2 = tmp2 * Fix_2_053119869;
- tmp3 = tmp3 * Fix_0_298631336;
- tmp10 = tmp10 * -Fix_0_899976223;
- tmp11 = tmp11 * -Fix_2_562915447;
- tmp12 = tmp12 * -Fix_0_390180644;
- tmp13 = tmp13 * -Fix_1_961570560;
-
- tmp12 += z1;
- tmp13 += z1;
- block[8 + x] = (tmp0 + tmp10 + tmp12) >> (Bits + Pass1Bits);
- block[24 + x] = (tmp1 + tmp11 + tmp13) >> (Bits + Pass1Bits);
- block[40 + x] = (tmp2 + tmp11 + tmp12) >> (Bits + Pass1Bits);
- block[56 + x] = (tmp3 + tmp10 + tmp13) >> (Bits + Pass1Bits);
- }
- }
- }
-}
diff --git a/src/ImageSharp/Formats/Jpg/Components/IDCT.cs b/src/ImageSharp/Formats/Jpg/Components/IDCT.cs
deleted file mode 100644
index 8c3a5a238..000000000
--- a/src/ImageSharp/Formats/Jpg/Components/IDCT.cs
+++ /dev/null
@@ -1,169 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp.Formats
-{
- ///
- /// Performs a 2-D Inverse Discrete Cosine Transformation.
- ///
- internal class IDCT
- {
- private const int W1 = 2841; // 2048*sqrt(2)*cos(1*pi/16)
- private const int W2 = 2676; // 2048*sqrt(2)*cos(2*pi/16)
- private const int W3 = 2408; // 2048*sqrt(2)*cos(3*pi/16)
- private const int W5 = 1609; // 2048*sqrt(2)*cos(5*pi/16)
- private const int W6 = 1108; // 2048*sqrt(2)*cos(6*pi/16)
- private const int W7 = 565; // 2048*sqrt(2)*cos(7*pi/16)
-
- private const int W1pw7 = W1 + W7;
- private const int W1mw7 = W1 - W7;
- private const int W2pw6 = W2 + W6;
- private const int W2mw6 = W2 - W6;
- private const int W3pw5 = W3 + W5;
- private const int W3mw5 = W3 - W5;
-
- private const int R2 = 181; // 256/sqrt(2)
-
- ///
- /// Performs a 2-D Inverse Discrete Cosine Transformation.
- ///
- /// The input coefficients should already have been multiplied by the
- /// appropriate quantization table. We use fixed-point computation, with the
- /// number of bits for the fractional component varying over the intermediate
- /// stages.
- ///
- /// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the
- /// discrete W transform and for the discrete Fourier transform", IEEE Trans. on
- /// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
- ///
- /// The source block of coefficients
- public static void Transform(ref Block src)
- {
- // Horizontal 1-D IDCT.
- for (int y = 0; y < 8; y++)
- {
- int y8 = y * 8;
-
- // If all the AC components are zero, then the IDCT is trivial.
- if (src[y8 + 1] == 0 && src[y8 + 2] == 0 && src[y8 + 3] == 0 &&
- src[y8 + 4] == 0 && src[y8 + 5] == 0 && src[y8 + 6] == 0 && src[y8 + 7] == 0)
- {
- int dc = src[y8 + 0] << 3;
- src[y8 + 0] = dc;
- src[y8 + 1] = dc;
- src[y8 + 2] = dc;
- src[y8 + 3] = dc;
- src[y8 + 4] = dc;
- src[y8 + 5] = dc;
- src[y8 + 6] = dc;
- src[y8 + 7] = dc;
- continue;
- }
-
- // Prescale.
- int x0 = (src[y8 + 0] << 11) + 128;
- int x1 = src[y8 + 4] << 11;
- int x2 = src[y8 + 6];
- int x3 = src[y8 + 2];
- int x4 = src[y8 + 1];
- int x5 = src[y8 + 7];
- int x6 = src[y8 + 5];
- int x7 = src[y8 + 3];
-
- // Stage 1.
- int x8 = W7 * (x4 + x5);
- x4 = x8 + (W1mw7 * x4);
- x5 = x8 - (W1pw7 * x5);
- x8 = W3 * (x6 + x7);
- x6 = x8 - (W3mw5 * x6);
- x7 = x8 - (W3pw5 * x7);
-
- // Stage 2.
- x8 = x0 + x1;
- x0 -= x1;
- x1 = W6 * (x3 + x2);
- x2 = x1 - (W2pw6 * x2);
- x3 = x1 + (W2mw6 * x3);
- x1 = x4 + x6;
- x4 -= x6;
- x6 = x5 + x7;
- x5 -= x7;
-
- // Stage 3.
- x7 = x8 + x3;
- x8 -= x3;
- x3 = x0 + x2;
- x0 -= x2;
- x2 = ((R2 * (x4 + x5)) + 128) >> 8;
- x4 = ((R2 * (x4 - x5)) + 128) >> 8;
-
- // Stage 4.
- src[y8 + 0] = (x7 + x1) >> 8;
- src[y8 + 1] = (x3 + x2) >> 8;
- src[y8 + 2] = (x0 + x4) >> 8;
- src[y8 + 3] = (x8 + x6) >> 8;
- src[y8 + 4] = (x8 - x6) >> 8;
- src[y8 + 5] = (x0 - x4) >> 8;
- src[y8 + 6] = (x3 - x2) >> 8;
- src[y8 + 7] = (x7 - x1) >> 8;
- }
-
- // Vertical 1-D IDCT.
- for (int x = 0; x < 8; x++)
- {
- // Similar to the horizontal 1-D IDCT case, if all the AC components are zero, then the IDCT is trivial.
- // However, after performing the horizontal 1-D IDCT, there are typically non-zero AC components, so
- // we do not bother to check for the all-zero case.
-
- // Prescale.
- int y0 = (src[x] << 8) + 8192;
- int y1 = src[32 + x] << 8;
- int y2 = src[48 + x];
- int y3 = src[16 + x];
- int y4 = src[8 + x];
- int y5 = src[56 + x];
- int y6 = src[40 + x];
- int y7 = src[24 + x];
-
- // Stage 1.
- int y8 = (W7 * (y4 + y5)) + 4;
- y4 = (y8 + (W1mw7 * y4)) >> 3;
- y5 = (y8 - (W1pw7 * y5)) >> 3;
- y8 = (W3 * (y6 + y7)) + 4;
- y6 = (y8 - (W3mw5 * y6)) >> 3;
- y7 = (y8 - (W3pw5 * y7)) >> 3;
-
- // Stage 2.
- y8 = y0 + y1;
- y0 -= y1;
- y1 = (W6 * (y3 + y2)) + 4;
- y2 = (y1 - (W2pw6 * y2)) >> 3;
- y3 = (y1 + (W2mw6 * y3)) >> 3;
- y1 = y4 + y6;
- y4 -= y6;
- y6 = y5 + y7;
- y5 -= y7;
-
- // Stage 3.
- y7 = y8 + y3;
- y8 -= y3;
- y3 = y0 + y2;
- y0 -= y2;
- y2 = ((R2 * (y4 + y5)) + 128) >> 8;
- y4 = ((R2 * (y4 - y5)) + 128) >> 8;
-
- // Stage 4.
- src[x] = (y7 + y1) >> 14;
- src[8 + x] = (y3 + y2) >> 14;
- src[16 + x] = (y0 + y4) >> 14;
- src[24 + x] = (y8 + y6) >> 14;
- src[32 + x] = (y8 - y6) >> 14;
- src[40 + x] = (y0 - y4) >> 14;
- src[48 + x] = (y3 - y2) >> 14;
- src[56 + x] = (y7 - y1) >> 14;
- }
- }
- }
-}
diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
index 960405530..b74ae2317 100644
--- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
@@ -189,7 +189,7 @@ namespace ImageSharp.Formats
this.huffmanTrees = new Huffman[(MaxTc + 1) * (MaxTh + 1)];
this.quantizationTables = new Block8x8F[MaxTq + 1];
- this.temp = new byte[2 * BlockF.BlockSize];
+ this.temp = new byte[2 * Block8x8F.ScalarCount];
this.componentArray = new Component[MaxComponents];
this.progCoeffs = new Block8x8F[MaxComponents][];
this.bits = default(Bits);
@@ -1053,32 +1053,32 @@ namespace ImageSharp.Formats
switch (x >> 4)
{
case 0:
- if (remaining < BlockF.BlockSize)
+ if (remaining < Block8x8F.ScalarCount)
{
done = true;
break;
}
- remaining -= BlockF.BlockSize;
- this.ReadFull(this.temp, 0, BlockF.BlockSize);
+ remaining -= Block8x8F.ScalarCount;
+ this.ReadFull(this.temp, 0, Block8x8F.ScalarCount);
- for (int i = 0; i < BlockF.BlockSize; i++)
+ for (int i = 0; i < Block8x8F.ScalarCount; i++)
{
this.quantizationTables[tq][i] = this.temp[i];
}
break;
case 1:
- if (remaining < 2 * BlockF.BlockSize)
+ if (remaining < 2 * Block8x8F.ScalarCount)
{
done = true;
break;
}
- remaining -= 2 * BlockF.BlockSize;
- this.ReadFull(this.temp, 0, 2 * BlockF.BlockSize);
+ remaining -= 2 * Block8x8F.ScalarCount;
+ this.ReadFull(this.temp, 0, 2 * Block8x8F.ScalarCount);
- for (int i = 0; i < BlockF.BlockSize; i++)
+ for (int i = 0; i < Block8x8F.ScalarCount; i++)
{
this.quantizationTables[tq][i] = (this.temp[2 * i] << 8) | this.temp[(2 * i) + 1];
}
@@ -1473,7 +1473,7 @@ namespace ImageSharp.Formats
// significant bit.
// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0.
int zigStart = 0;
- int zigEnd = BlockF.BlockSize - 1;
+ int zigEnd = Block8x8F.ScalarCount - 1;
int ah = 0;
int al = 0;
@@ -1484,7 +1484,7 @@ namespace ImageSharp.Formats
ah = this.temp[3 + scanComponentCountX2] >> 4;
al = this.temp[3 + scanComponentCountX2] & 0x0f;
- if ((zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || zigEnd >= BlockF.BlockSize)
+ if ((zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || zigEnd >= Block8x8F.ScalarCount)
{
throw new ImageFormatException("Bad spectral selection bounds");
}
@@ -1788,7 +1788,7 @@ namespace ImageSharp.Formats
if (this.isProgressive)
{
- if (zigEnd != BlockF.BlockSize - 1 || al != 0)
+ if (zigEnd != Block8x8F.ScalarCount - 1 || al != 0)
{
// We haven't completely decoded this 8x8 block. Save the coefficients.
diff --git a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
index d6c387911..8c5ed8b90 100644
--- a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
@@ -561,7 +561,7 @@ namespace ImageSharp.Formats
HuffIndex h = (HuffIndex)((2 * (int)index) + 1);
int runLength = 0;
- for (int zig = 1; zig < Block.BlockSize; zig++)
+ for (int zig = 1; zig < Block8x8F.ScalarCount; zig++)
{
float ac = d[zig];
@@ -679,12 +679,12 @@ namespace ImageSharp.Formats
private void WriteDefineQuantizationTables()
{
// Marker + quantization table lengths
- int markerlen = 2 + (QuantizationTableCount * (1 + Block.BlockSize));
+ int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.ScalarCount));
this.WriteMarkerHeader(JpegConstants.Markers.DQT, markerlen);
// Loop through and collect the tables as one array.
// This allows us to reduce the number of writes to the stream.
- byte[] dqt = new byte[(QuantizationTableCount * Block.BlockSize) + QuantizationTableCount];
+ byte[] dqt = new byte[(QuantizationTableCount * Block8x8F.ScalarCount) + QuantizationTableCount];
int offset = 0;
WriteDataToDqt(dqt, ref offset, QuantIndex.Luminance, ref this.luminanceQuantTable);
diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs
index a3c2eba73..68797c778 100644
--- a/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs
+++ b/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs
@@ -1,106 +1,38 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
+using System.Collections.Generic;
namespace ImageSharp.Benchmarks.Image
{
- using System;
- using System.Collections.Generic;
using System.Drawing;
using System.IO;
- using System.Linq;
-
using BenchmarkDotNet.Attributes;
using Image = ImageSharp.Image;
using ImageSharpSize = ImageSharp.Size;
- public class DecodeJpegMultiple
+ public class DecodeJpegMultiple : MultiImageBenchmarkBase
{
- private const string Folder = "../ImageSharp.Tests/TestImages/Formats/Jpg/";
-
- private Dictionary fileNamesToBytes;
-
- public enum JpegTestingMode
+ protected override IEnumerable InputImageSubfolders => new[]
{
- All,
-
- SmallImagesOnly,
-
- LargeImagesOnly,
+ "Formats/Jpg/"
+ };
- CalliphoraOnly,
- }
-
- [Params(JpegTestingMode.All, JpegTestingMode.SmallImagesOnly, JpegTestingMode.LargeImagesOnly,
- JpegTestingMode.CalliphoraOnly)]
- public JpegTestingMode Mode { get; set; }
-
- private IEnumerable> RequestedImages
- {
- get
- {
- int thresholdInBytes = 100000;
-
- switch (this.Mode)
- {
- case JpegTestingMode.All:
- return this.fileNamesToBytes;
- case JpegTestingMode.SmallImagesOnly:
- return this.fileNamesToBytes.Where(kv => kv.Value.Length < thresholdInBytes);
- case JpegTestingMode.LargeImagesOnly:
- return this.fileNamesToBytes.Where(kv => kv.Value.Length >= thresholdInBytes);
- case JpegTestingMode.CalliphoraOnly:
- return new[] { this.fileNamesToBytes.First(kv => kv.Key.ToLower().Contains("calliphora")) };
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- }
+ protected override IEnumerable FileFilters => new[] { "*.jpg" };
[Benchmark(Description = "DecodeJpegMultiple - ImageSharp")]
- public ImageSharpSize JpegImageSharp()
+ public void DecodeJpegImageSharp()
{
- ImageSharpSize lastSize = new ImageSharpSize();
- foreach (var kv in this.RequestedImages)
- {
- using (MemoryStream memoryStream = new MemoryStream(kv.Value))
- {
- Image image = new Image(memoryStream);
- lastSize = new ImageSharpSize(image.Width, image.Height);
- }
- }
-
- return lastSize;
+ this.ForEachStream(
+ ms => new ImageSharp.Image(ms)
+ );
}
[Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")]
- public Size JpegSystemDrawing()
+ public void DecodeJpegSystemDrawing()
{
- Size lastSize = new Size();
- foreach (var kv in this.RequestedImages)
- {
- using (MemoryStream memoryStream = new MemoryStream(kv.Value))
- {
- using (System.Drawing.Image image = System.Drawing.Image.FromStream(memoryStream))
- {
- lastSize = image.Size;
- }
- }
- }
-
- return lastSize;
+ this.ForEachStream(
+ System.Drawing.Image.FromStream
+ );
}
- [Setup]
- public void ReadImages()
- {
- if (this.fileNamesToBytes != null) return;
-
- var allFiles = Directory.EnumerateFiles(Folder, "*.jpg", SearchOption.AllDirectories).ToArray();
-
- this.fileNamesToBytes = allFiles.ToDictionary(fn => fn, File.ReadAllBytes);
- }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Image/EncodeJpegMultiple.cs
new file mode 100644
index 000000000..d6af8c842
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Image/EncodeJpegMultiple.cs
@@ -0,0 +1,50 @@
+namespace ImageSharp.Benchmarks.Image
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Drawing.Imaging;
+ using System.IO;
+ using System.Linq;
+
+ using BenchmarkDotNet.Attributes;
+ using BenchmarkDotNet.Attributes.Jobs;
+ using BenchmarkDotNet.Engines;
+
+ using ImageSharp.Formats;
+
+ public class EncodeJpegMultiple : MultiImageBenchmarkBase.WithImagesPreloaded
+ {
+ protected override IEnumerable InputImageSubfolders => new[]
+ {
+ "Formats/Bmp/",
+ "Formats/Jpg/baseline"
+ };
+
+ protected override IEnumerable FileFilters => new[] { "*.bmp", "*.jpg" };
+
+ [Benchmark(Description = "EncodeJpegMultiple - ImageSharp")]
+ public void EncodeJpegImageSharp()
+ {
+ this.ForEachImageSharpImage(
+ img =>
+ {
+ MemoryStream ms = new MemoryStream();
+ img.Save(ms, new JpegEncoder());
+ return ms;
+ });
+ }
+
+ [Benchmark(Baseline = true, Description = "EncodeJpegMultiple - System.Drawing")]
+ public void EncodeJpegSystemDrawing()
+ {
+ this.ForEachSystemDrawingImage(
+ img =>
+ {
+ MemoryStream ms = new MemoryStream();
+ img.Save(ms, ImageFormat.Jpeg);
+ return ms;
+ });
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs
new file mode 100644
index 000000000..d605bf931
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs
@@ -0,0 +1,185 @@
+namespace ImageSharp.Benchmarks.Image
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Drawing;
+ using System.IO;
+ using System.Linq;
+ using System.Numerics;
+
+ using BenchmarkDotNet.Attributes;
+
+ using Image = ImageSharp.Image;
+
+ public abstract class MultiImageBenchmarkBase
+ {
+ protected Dictionary FileNamesToBytes = new Dictionary();
+
+ protected Dictionary FileNamesToImageSharpImages = new Dictionary();
+ protected Dictionary FileNamesToSystemDrawingImages = new Dictionary();
+
+ public enum TestingMode
+ {
+ All,
+
+ SmallImagesOnly,
+
+ LargeImagesOnly
+ }
+
+ [Params(TestingMode.All, TestingMode.SmallImagesOnly, TestingMode.LargeImagesOnly)]
+ public TestingMode Mode { get; set; }
+
+ protected virtual string BaseFolder => "../ImageSharp.Tests/TestImages/";
+
+ protected abstract IEnumerable FileFilters { get; }
+
+ protected IEnumerable FilterWords => new string[] { };
+
+ protected virtual IEnumerable Folders => this.InputImageSubfolders.Select(f => Path.Combine(this.BaseFolder, f));
+
+ protected virtual int LargeImageThresholdInBytes => 100000;
+
+ protected IEnumerable> EnumeratePairsByBenchmarkSettings(
+ Dictionary input,
+ Predicate checkIfSmall)
+ {
+ switch (this.Mode)
+ {
+ case TestingMode.All:
+ return input;
+ case TestingMode.SmallImagesOnly:
+ return input.Where(kv => checkIfSmall(kv.Value));
+ case TestingMode.LargeImagesOnly:
+ return input.Where(kv => !checkIfSmall(kv.Value));
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ protected IEnumerable> FileNames2Bytes
+ =>
+ this.EnumeratePairsByBenchmarkSettings(
+ this.FileNamesToBytes,
+ arr => arr.Length < this.LargeImageThresholdInBytes);
+
+ protected abstract IEnumerable InputImageSubfolders { get; }
+
+ [Setup]
+ public void ReadImages()
+ {
+ //Console.WriteLine("Vector.IsHardwareAccelerated: " + Vector.IsHardwareAccelerated);
+ this.ReadImagesImpl();
+ }
+
+ protected virtual void ReadImagesImpl()
+ {
+ foreach (string folder in this.Folders)
+ {
+ var allFiles =
+ this.FileFilters.SelectMany(
+ f =>
+ Directory.EnumerateFiles(folder, f, SearchOption.AllDirectories)
+ .Where(fn => !this.FilterWords.Any(w => fn.ToLower().Contains(w)))).ToArray();
+ foreach (var fn in allFiles)
+ {
+ this.FileNamesToBytes[fn] = File.ReadAllBytes(fn);
+ }
+ }
+ }
+
+ protected void ForEachStream(Func operation)
+ {
+ foreach (var kv in this.FileNames2Bytes)
+ {
+ using (MemoryStream memoryStream = new MemoryStream(kv.Value))
+ {
+ try
+ {
+ var obj = operation(memoryStream);
+ (obj as IDisposable)?.Dispose();
+
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}");
+ }
+ }
+ }
+ }
+
+ public abstract class WithImagesPreloaded : MultiImageBenchmarkBase
+ {
+ protected override void ReadImagesImpl()
+ {
+ base.ReadImagesImpl();
+
+ foreach (var kv in this.FileNamesToBytes)
+ {
+ byte[] bytes = kv.Value;
+ string fn = kv.Key;
+
+ using (var ms1 = new MemoryStream(bytes))
+ {
+ this.FileNamesToImageSharpImages[fn] = new Image(ms1);
+
+ }
+
+ this.FileNamesToSystemDrawingImages[fn] = new Bitmap(new MemoryStream(bytes));
+ }
+ }
+
+ protected IEnumerable> FileNames2ImageSharpImages
+ =>
+ this.EnumeratePairsByBenchmarkSettings(
+ this.FileNamesToImageSharpImages,
+ img => img.Width * img.Height < this.LargeImageThresholdInPixels);
+
+ protected IEnumerable> FileNames2SystemDrawingImages
+ =>
+ this.EnumeratePairsByBenchmarkSettings(
+ this.FileNamesToSystemDrawingImages,
+ img => img.Width * img.Height < this.LargeImageThresholdInPixels);
+
+ protected virtual int LargeImageThresholdInPixels => 700000;
+
+ protected void ForEachImageSharpImage(Func operation)
+ {
+ foreach (var kv in this.FileNames2ImageSharpImages)
+ {
+ try
+ {
+ var obj = operation(kv.Value);
+ (obj as IDisposable)?.Dispose();
+
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}");
+ }
+
+ }
+ }
+
+ protected void ForEachSystemDrawingImage(Func operation)
+ {
+ foreach (var kv in this.FileNames2SystemDrawingImages)
+ {
+ try
+ {
+ var obj = operation(kv.Value);
+ (obj as IDisposable)?.Dispose();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}");
+ }
+ }
+ }
+ }
+
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
index 265d6b5f2..d117cfd0a 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
@@ -120,25 +120,6 @@ namespace ImageSharp.Tests.Formats.Jpg
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f));
}
- }
-
- [Theory]
- [InlineData(42)]
- [InlineData(1)]
- [InlineData(2)]
- public void Fdct_SimdReferenceImplementation_IsEquivalentToFloatingPointReferenceImplementation(int seed)
- {
- Block classic = new Block() { Data = Create8x8RandomIntData(-200, 200, seed) };
- MutableSpan src = new MutableSpan(classic.Data).ConvertToFloat32MutableSpan();
-
- MutableSpan dest1 = new MutableSpan(64);
- MutableSpan dest2 = new MutableSpan(64);
- MutableSpan temp = new MutableSpan(64);
-
- ReferenceImplementations.fDCT2D_llm(src, dest1, temp, downscaleBy8: true, offsetSourceByNeg128: false);
- ReferenceImplementations.fDCT8x8_llm_sse(src, dest2, temp);
-
- Assert.Equal(dest1.Data, dest2.Data, new ApproximateFloatComparer(1f));
- }
+ }
}
}
\ No newline at end of file