diff --git a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
index 66ef146e44..cfea24ad99 100644
--- a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
@@ -103,6 +103,22 @@ namespace ImageSharp.Formats
Marshal.Copy((IntPtr)blockPtr, dest.Data, dest.Offset, ScalarCount);
}
+ ///
+ /// Convert salars to byte-s and copy to dest
+ ///
+ /// Pointer to block
+ /// Destination
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan dest)
+ {
+ float* fPtr = (float*)blockPtr;
+ for (int i = 0; i < ScalarCount; i++)
+ {
+ dest[i] = (byte) *fPtr;
+ fPtr++;
+ }
+ }
+
///
/// Load raw 32bit floating point data from source
///
@@ -128,7 +144,7 @@ namespace ImageSharp.Formats
Marshal.Copy((IntPtr)ptr, dest.Data, dest.Offset, ScalarCount);
}
}
-
+
///
/// Copy raw 32bit floating point data to dest
///
diff --git a/src/ImageSharp/Formats/Jpg/Components/HuffIndex.cs b/src/ImageSharp/Formats/Jpg/Components/HuffIndex.cs
new file mode 100644
index 0000000000..ff2a888a47
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpg/Components/HuffIndex.cs
@@ -0,0 +1,32 @@
+namespace ImageSharp.Formats.Jpg.Components
+{
+ ///
+ /// Enumerates the Huffman tables
+ ///
+ internal enum HuffIndex
+ {
+ ///
+ /// The DC luminance huffman table index
+ ///
+ LuminanceDC = 0,
+
+ // ReSharper disable UnusedMember.Local
+
+ ///
+ /// The AC luminance huffman table index
+ ///
+ LuminanceAC = 1,
+
+ ///
+ /// The DC chrominance huffman table index
+ ///
+ ChrominanceDC = 2,
+
+ ///
+ /// The AC chrominance huffman table index
+ ///
+ ChrominanceAC = 3,
+
+ // ReSharper restore UnusedMember.Local
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/Components/HuffmanLut.cs b/src/ImageSharp/Formats/Jpg/Components/HuffmanLut.cs
new file mode 100644
index 0000000000..a53ebf61bf
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpg/Components/HuffmanLut.cs
@@ -0,0 +1,68 @@
+namespace ImageSharp.Formats.Jpg.Components
+{
+ ///
+ /// A compiled look-up table representation of a huffmanSpec.
+ /// Each value maps to a uint32 of which the 8 most significant bits hold the
+ /// codeword size in bits and the 24 least significant bits hold the codeword.
+ /// The maximum codeword size is 16 bits.
+ ///
+ internal struct HuffmanLut
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The encoding specifications.
+ public HuffmanLut(HuffmanSpec spec)
+ {
+ int maxValue = 0;
+
+ foreach (byte v in spec.Values)
+ {
+ if (v > maxValue)
+ {
+ maxValue = v;
+ }
+ }
+
+ this.Values = new uint[maxValue + 1];
+
+ int code = 0;
+ int k = 0;
+
+ for (int i = 0; i < spec.Count.Length; i++)
+ {
+ int bits = (i + 1) << 24;
+ for (int j = 0; j < spec.Count[i]; j++)
+ {
+ this.Values[spec.Values[k]] = (uint)(bits | code);
+ code++;
+ k++;
+ }
+
+ code <<= 1;
+ }
+ }
+
+ ///
+ /// Initialize static members
+ ///
+ static HuffmanLut()
+ {
+ // Initialize the Huffman tables
+ for (int i = 0; i < HuffmanSpec.TheHuffmanSpecs.Length; i++)
+ {
+ HuffmanLut.TheHuffmanLut[i] = new HuffmanLut(HuffmanSpec.TheHuffmanSpecs[i]);
+ }
+ }
+
+ ///
+ /// Gets the collection of huffman values.
+ ///
+ public uint[] Values { get; }
+
+ ///
+ /// The compiled representations of theHuffmanSpec.
+ ///
+ public static readonly HuffmanLut[] TheHuffmanLut = new HuffmanLut[4];
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/Components/HuffmanSpec.cs b/src/ImageSharp/Formats/Jpg/Components/HuffmanSpec.cs
new file mode 100644
index 0000000000..909d7da031
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpg/Components/HuffmanSpec.cs
@@ -0,0 +1,112 @@
+namespace ImageSharp.Formats.Jpg.Components
+{
+ ///
+ /// The Huffman encoding specifications.
+ ///
+ internal struct HuffmanSpec
+ {
+ ///
+ /// Gets count[i] - The number of codes of length i bits.
+ ///
+ public readonly byte[] Count;
+
+ ///
+ /// Gets value[i] - The decoded value of the codeword at the given index.
+ ///
+ public readonly byte[] Values;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The number of codes.
+ /// The decoded values.
+ public HuffmanSpec(byte[] count, byte[] values)
+ {
+ this.Count = count;
+ this.Values = values;
+ }
+
+#pragma warning disable SA1118 // ParameterMustNotSpanMultipleLines
+ ///
+ /// The Huffman encoding specifications.
+ /// This encoder uses the same Huffman encoding for all images.
+ ///
+ public static readonly HuffmanSpec[] TheHuffmanSpecs =
+ {
+ // Luminance DC.
+ new HuffmanSpec(
+ new byte[]
+ {
+ 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
+ },
+ new byte[]
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+ }),
+ new HuffmanSpec(
+ new byte[]
+ {
+ 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125
+ },
+ new byte[]
+ {
+ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
+ 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71,
+ 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1,
+ 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
+ 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+ 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83,
+ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
+ 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa
+ }),
+ new HuffmanSpec(
+ new byte[]
+ {
+ 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
+ },
+ new byte[]
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+ }),
+
+ // Chrominance AC.
+ new HuffmanSpec(
+ new byte[]
+ {
+ 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119
+ },
+ new byte[]
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31,
+ 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22,
+ 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1,
+ 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
+ 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18,
+ 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+ 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+ 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
+ 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
+ 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
+ 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
+ 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+ 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa
+ })
+ };
+#pragma warning restore SA1118 // ParameterMustNotSpanMultipleLines
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/Components/QuantIndex.cs b/src/ImageSharp/Formats/Jpg/Components/QuantIndex.cs
new file mode 100644
index 0000000000..11a82f831e
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpg/Components/QuantIndex.cs
@@ -0,0 +1,18 @@
+namespace ImageSharp.Formats.Jpg.Components
+{
+ ///
+ /// Enumerates the quantization tables
+ ///
+ internal enum QuantIndex
+ {
+ ///
+ /// The luminance quantization table index
+ ///
+ Luminance = 0,
+
+ ///
+ /// The chrominance quantization table index
+ ///
+ Chrominance = 1,
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
index 8c5ed8b90e..75c38ae5aa 100644
--- a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
@@ -2,14 +2,16 @@
// 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.IO;
using System.Numerics;
using System.Runtime.CompilerServices;
+ using ImageSharp.Formats.Jpg.Components;
+
///
/// Image encoder for writing an image to a stream as a jpeg.
///
@@ -20,95 +22,6 @@ namespace ImageSharp.Formats
///
private const int QuantizationTableCount = 2;
-#pragma warning disable SA1118 // ParameterMustNotSpanMultipleLines
-
- ///
- /// The Huffman encoding specifications.
- /// This encoder uses the same Huffman encoding for all images.
- ///
- private static readonly HuffmanSpec[] TheHuffmanSpecs =
- {
- // Luminance DC.
- new HuffmanSpec(
- new byte[]
- {
- 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
- },
- new byte[]
- {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
- }),
- new HuffmanSpec(
- new byte[]
- {
- 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125
- },
- new byte[]
- {
- 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
- 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71,
- 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1,
- 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
- 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
- 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37,
- 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
- 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
- 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83,
- 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
- 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
- 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
- 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
- 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
- 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
- 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
- 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa
- }),
- new HuffmanSpec(
- new byte[]
- {
- 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
- },
- new byte[]
- {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
- }),
-
- // Chrominance AC.
- new HuffmanSpec(
- new byte[]
- {
- 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119
- },
- new byte[]
- {
- 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31,
- 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22,
- 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1,
- 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
- 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18,
- 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36,
- 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47,
- 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
- 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
- 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
- 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
- 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
- 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
- 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
- 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
- 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
- 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
- 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa
- })
- };
-#pragma warning restore SA1118 // ParameterMustNotSpanMultipleLines
-
- ///
- /// The compiled representations of theHuffmanSpec.
- ///
- private static readonly HuffmanLut[] TheHuffmanLut = new HuffmanLut[4];
-
///
/// Counts the number of bits needed to hold an integer.
///
@@ -129,32 +42,6 @@ namespace ImageSharp.Formats
8, 8, 8,
};
- ///
- /// The unscaled quantization tables in zig-zag order. Each
- /// encoder copies and scales the tables according to its quality parameter.
- /// The values are derived from section K.1 after converting from natural to
- /// zig-zag order.
- ///
- private static readonly byte[,] UnscaledQuant =
- {
- {
- // Luminance.
- 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24,
- 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60,
- 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80,
- 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112,
- 100, 120, 92, 101, 103, 99,
- },
- {
- // Chrominance.
- 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99,
- }
- };
-
///
/// The SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes:
/// - the marker length "\x00\x0c",
@@ -168,7 +55,9 @@ namespace ImageSharp.Formats
///
private static readonly byte[] SosHeaderYCbCr =
{
- JpegConstants.Markers.XFF, JpegConstants.Markers.SOS, // Marker
+ JpegConstants.Markers.XFF, JpegConstants.Markers.SOS,
+
+ // Marker
0x00, 0x0c,
// Length (high byte, low byte), must be 6 + 2 * (number of components in scan)
@@ -181,7 +70,35 @@ namespace ImageSharp.Formats
0x11, // DC/AC Huffman table
0x00, // Ss - Start of spectral selection.
0x3f, // Se - End of spectral selection.
- 0x00 // Ah + Ah (Successive approximation bit position high + low)
+ 0x00
+
+ // Ah + Ah (Successive approximation bit position high + low)
+ };
+
+ ///
+ /// The unscaled quantization tables in zig-zag order. Each
+ /// encoder copies and scales the tables according to its quality parameter.
+ /// The values are derived from section K.1 after converting from natural to
+ /// zig-zag order.
+ ///
+ private static readonly byte[,] UnscaledQuant =
+ {
+ {
+ // Luminance.
+ 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24,
+ 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60,
+ 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80,
+ 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112,
+ 100, 120, 92, 101, 103, 99,
+ },
+ {
+ // Chrominance.
+ 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ }
};
///
@@ -199,16 +116,6 @@ namespace ImageSharp.Formats
///
private readonly byte[] huffmanBuffer = new byte[179];
- ///
- /// The scaled luminance table, in zig-zag order.
- ///
- private Block8x8F luminanceQuantTable;
-
- ///
- /// The scaled chrominance table, in zig-zag order.
- ///
- private Block8x8F chrominanceQuantTable;
-
///
/// The accumulated bits to write to the stream.
///
@@ -220,72 +127,24 @@ namespace ImageSharp.Formats
private uint bitCount;
///
- /// The output stream. All attempted writes after the first error become no-ops.
- ///
- private Stream outputStream;
-
- ///
- /// The subsampling method to use.
+ /// The scaled chrominance table, in zig-zag order.
///
- private JpegSubsample subsample;
+ private Block8x8F chrominanceQuantTable;
///
- /// Initializes static members of the class.
+ /// The scaled luminance table, in zig-zag order.
///
- static JpegEncoderCore()
- {
- // Initialize the Huffman tables
- for (int i = 0; i < TheHuffmanSpecs.Length; i++)
- {
- TheHuffmanLut[i] = new HuffmanLut(TheHuffmanSpecs[i]);
- }
- }
+ private Block8x8F luminanceQuantTable;
///
- /// Enumerates the Huffman tables
+ /// The output stream. All attempted writes after the first error become no-ops.
///
- private enum HuffIndex
- {
- ///
- /// The DC luminance huffman table index
- ///
- LuminanceDC = 0,
-
- // ReSharper disable UnusedMember.Local
-
- ///
- /// The AC luminance huffman table index
- ///
- LuminanceAC = 1,
-
- ///
- /// The DC chrominance huffman table index
- ///
- ChrominanceDC = 2,
-
- ///
- /// The AC chrominance huffman table index
- ///
- ChrominanceAC = 3,
-
- // ReSharper restore UnusedMember.Local
- }
+ private Stream outputStream;
///
- /// Enumerates the quantization tables
+ /// The subsampling method to use.
///
- private enum QuantIndex
- {
- ///
- /// The luminance quantization table index
- ///
- Luminance = 0,
-
- ///
- /// The chrominance quantization table index
- ///
- Chrominance = 1,
- }
+ private JpegSubsample subsample;
///
/// Encode writes the image to the jpeg baseline format with the given options.
@@ -403,8 +262,7 @@ namespace ImageSharp.Formats
Block8x8F* yBlock,
Block8x8F* cbBlock,
Block8x8F* crBlock,
- PixelArea rgbBytes)
- where TColor : struct, IPackedPixel, IEquatable
+ PixelArea rgbBytes) where TColor : struct, IPackedPixel, IEquatable
{
float* yBlockRaw = (float*)yBlock;
float* cbBlockRaw = (float*)cbBlock;
@@ -438,6 +296,18 @@ namespace ImageSharp.Formats
}
}
+#pragma warning disable SA1204
+ private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F q)
+ {
+ dqt[offset++] = (byte)i;
+ for (int j = 0; j < Block8x8F.ScalarCount; j++)
+ {
+ dqt[offset++] = (byte)q[j];
+ }
+ }
+
+#pragma warning restore SA1204
+
///
/// Emits the least significant count of bits of bits to the bit-stream.
/// The precondition is bits < 1<<nBits && nBits <= 16.
@@ -486,7 +356,7 @@ namespace ImageSharp.Formats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EmitHuff(HuffIndex index, int value)
{
- uint x = TheHuffmanLut[(int)index].Values[value];
+ uint x = HuffmanLut.TheHuffmanLut[(int)index].Values[value];
this.Emit(x & ((1 << 24) - 1), x >> 24);
}
@@ -524,6 +394,107 @@ namespace ImageSharp.Formats
}
}
+ ///
+ /// Encodes the image with no subsampling.
+ ///
+ /// The pixel format.
+ /// The pixel accessor providing access to the image pixels.
+ private void Encode444(PixelAccessor pixels)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ // TODO: Need a JpegEncoderScanImpl struct to encapsulate all this mess:
+ Block8x8F b = default(Block8x8F);
+ Block8x8F cb = default(Block8x8F);
+ Block8x8F cr = default(Block8x8F);
+
+ Block8x8F temp1 = default(Block8x8F);
+ Block8x8F temp2 = default(Block8x8F);
+
+ Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
+ Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
+
+ UnzigData unzig = UnzigData.Create();
+
+ // ReSharper disable once InconsistentNaming
+ float prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
+
+ using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.XYZ, true))
+ {
+ for (int y = 0; y < pixels.Height; y += 8)
+ {
+ for (int x = 0; x < pixels.Width; x += 8)
+ {
+ ToYCbCr(pixels, x, y, &b, &cb, &cr, rgbBytes);
+
+ prevDCY = this.WriteBlock(
+ QuantIndex.Luminance,
+ prevDCY,
+ &b,
+ &temp1,
+ &temp2,
+ &onStackLuminanceQuantTable,
+ unzig.Data);
+ prevDCCb = this.WriteBlock(
+ QuantIndex.Chrominance,
+ prevDCCb,
+ &cb,
+ &temp1,
+ &temp2,
+ &onStackChrominanceQuantTable,
+ unzig.Data);
+ prevDCCr = this.WriteBlock(
+ QuantIndex.Chrominance,
+ prevDCCr,
+ &cr,
+ &temp1,
+ &temp2,
+ &onStackChrominanceQuantTable,
+ unzig.Data);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Writes the application header containing the JFIF identifier plus extra data.
+ ///
+ /// The resolution of the image in the x- direction.
+ /// The resolution of the image in the y- direction.
+ private void WriteApplicationHeader(short horizontalResolution, short verticalResolution)
+ {
+ // Write the start of image marker. Markers are always prefixed with with 0xff.
+ this.buffer[0] = JpegConstants.Markers.XFF;
+ this.buffer[1] = JpegConstants.Markers.SOI;
+
+ // Write the JFIF headers
+ this.buffer[2] = JpegConstants.Markers.XFF;
+ this.buffer[3] = JpegConstants.Markers.APP0; // Application Marker
+ this.buffer[4] = 0x00;
+ this.buffer[5] = 0x10;
+ this.buffer[6] = 0x4a; // J
+ this.buffer[7] = 0x46; // F
+ this.buffer[8] = 0x49; // I
+ this.buffer[9] = 0x46; // F
+ this.buffer[10] = 0x00; // = "JFIF",'\0'
+ this.buffer[11] = 0x01; // versionhi
+ this.buffer[12] = 0x01; // versionlo
+ this.buffer[13] = 0x01; // xyunits as dpi
+
+ // No thumbnail
+ this.buffer[14] = 0x00; // Thumbnail width
+ this.buffer[15] = 0x00; // Thumbnail height
+
+ this.outputStream.Write(this.buffer, 0, 16);
+
+ // Resolution. Big Endian
+ this.buffer[0] = (byte)(horizontalResolution >> 8);
+ this.buffer[1] = (byte)horizontalResolution;
+ this.buffer[2] = (byte)(verticalResolution >> 8);
+ this.buffer[3] = (byte)verticalResolution;
+
+ this.outputStream.Write(this.buffer, 0, 4);
+ }
+
///
/// Writes a block of pixel data using the given quantization table,
/// returning the post-quantized DC value of the DCT-transformed block.
@@ -591,54 +562,74 @@ namespace ImageSharp.Formats
}
///
- /// Writes the application header containing the JFIF identifier plus extra data.
+ /// Writes the Define Huffman Table marker and tables.
///
- /// The resolution of the image in the x- direction.
- /// The resolution of the image in the y- direction.
- private void WriteApplicationHeader(short horizontalResolution, short verticalResolution)
+ /// The number of components to write.
+ private void WriteDefineHuffmanTables(int componentCount)
{
- // Write the start of image marker. Markers are always prefixed with with 0xff.
- this.buffer[0] = JpegConstants.Markers.XFF;
- this.buffer[1] = JpegConstants.Markers.SOI;
+ // Table identifiers.
+ byte[] headers = { 0x00, 0x10, 0x01, 0x11 };
+ int markerlen = 2;
+ HuffmanSpec[] specs = HuffmanSpec.TheHuffmanSpecs;
- // Write the JFIF headers
- this.buffer[2] = JpegConstants.Markers.XFF;
- this.buffer[3] = JpegConstants.Markers.APP0; // Application Marker
- this.buffer[4] = 0x00;
- this.buffer[5] = 0x10;
- this.buffer[6] = 0x4a; // J
- this.buffer[7] = 0x46; // F
- this.buffer[8] = 0x49; // I
- this.buffer[9] = 0x46; // F
- this.buffer[10] = 0x00; // = "JFIF",'\0'
- this.buffer[11] = 0x01; // versionhi
- this.buffer[12] = 0x01; // versionlo
- this.buffer[13] = 0x01; // xyunits as dpi
+ if (componentCount == 1)
+ {
+ // Drop the Chrominance tables.
+ specs = new[] { HuffmanSpec.TheHuffmanSpecs[0], HuffmanSpec.TheHuffmanSpecs[1] };
+ }
- // No thumbnail
- this.buffer[14] = 0x00; // Thumbnail width
- this.buffer[15] = 0x00; // Thumbnail height
+ foreach (HuffmanSpec s in specs)
+ {
+ markerlen += 1 + 16 + s.Values.Length;
+ }
- this.outputStream.Write(this.buffer, 0, 16);
+ this.WriteMarkerHeader(JpegConstants.Markers.DHT, markerlen);
+ for (int i = 0; i < specs.Length; i++)
+ {
+ HuffmanSpec spec = specs[i];
+ int len = 0;
- // Resolution. Big Endian
- this.buffer[0] = (byte)(horizontalResolution >> 8);
- this.buffer[1] = (byte)horizontalResolution;
- this.buffer[2] = (byte)(verticalResolution >> 8);
- this.buffer[3] = (byte)verticalResolution;
+ fixed (byte* huffman = this.huffmanBuffer)
+ fixed (byte* count = spec.Count)
+ fixed (byte* values = spec.Values)
+ {
+ huffman[len++] = headers[i];
- this.outputStream.Write(this.buffer, 0, 4);
+ for (int c = 0; c < spec.Count.Length; c++)
+ {
+ huffman[len++] = count[c];
+ }
+
+ for (int v = 0; v < spec.Values.Length; v++)
+ {
+ huffman[len++] = values[v];
+ }
+ }
+
+ this.outputStream.Write(this.huffmanBuffer, 0, len);
+ }
}
///
- /// Writes the metadata profiles to the image.
+ /// Writes the Define Quantization Marker and tables.
///
- /// The image.
- /// The pixel format.
- private void WriteProfiles(Image image)
- where TColor : struct, IPackedPixel, IEquatable
+ private void WriteDefineQuantizationTables()
{
- this.WriteProfile(image.ExifProfile);
+ // Marker + quantization table lengths
+ 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.
+ int dqtCount = (QuantizationTableCount * Block8x8F.ScalarCount) + QuantizationTableCount;
+ byte[] dqt = ArrayPool.Shared.Rent(dqtCount);
+ int offset = 0;
+
+ WriteDataToDqt(dqt, ref offset, QuantIndex.Luminance, ref this.luminanceQuantTable);
+ WriteDataToDqt(dqt, ref offset, QuantIndex.Chrominance, ref this.chrominanceQuantTable);
+
+ this.outputStream.Write(dqt, 0, dqtCount);
+ ArrayPool.Shared.Return(dqt);
}
///
@@ -674,35 +665,14 @@ namespace ImageSharp.Formats
}
///
- /// Writes the Define Quantization Marker and tables.
+ /// Writes the metadata profiles to the image.
///
- private void WriteDefineQuantizationTables()
- {
- // Marker + quantization table lengths
- 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 * Block8x8F.ScalarCount) + QuantizationTableCount];
- int offset = 0;
-
- WriteDataToDqt(dqt, ref offset, QuantIndex.Luminance, ref this.luminanceQuantTable);
- WriteDataToDqt(dqt, ref offset, QuantIndex.Chrominance, ref this.chrominanceQuantTable);
-
- this.outputStream.Write(dqt, 0, dqt.Length);
- }
-
-#pragma warning disable SA1204
- private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F q)
+ /// The image.
+ /// The pixel format.
+ private void WriteProfiles(Image image) where TColor : struct, IPackedPixel, IEquatable
{
- dqt[offset++] = (byte)i;
- for (int j = 0; j < Block8x8F.ScalarCount; j++)
- {
- dqt[offset++] = (byte)q[j];
- }
+ this.WriteProfile(image.ExifProfile);
}
-#pragma warning restore SA1204
///
/// Writes the Start Of Frame (Baseline) marker
@@ -760,55 +730,6 @@ namespace ImageSharp.Formats
this.outputStream.Write(this.buffer, 0, (3 * (componentCount - 1)) + 9);
}
- ///
- /// Writes the Define Huffman Table marker and tables.
- ///
- /// The number of components to write.
- private void WriteDefineHuffmanTables(int componentCount)
- {
- // Table identifiers.
- byte[] headers = { 0x00, 0x10, 0x01, 0x11 };
- int markerlen = 2;
- HuffmanSpec[] specs = TheHuffmanSpecs;
-
- if (componentCount == 1)
- {
- // Drop the Chrominance tables.
- specs = new[] { TheHuffmanSpecs[0], TheHuffmanSpecs[1] };
- }
-
- foreach (HuffmanSpec s in specs)
- {
- markerlen += 1 + 16 + s.Values.Length;
- }
-
- this.WriteMarkerHeader(JpegConstants.Markers.DHT, markerlen);
- for (int i = 0; i < specs.Length; i++)
- {
- HuffmanSpec spec = specs[i];
- int len = 0;
-
- fixed (byte* huffman = this.huffmanBuffer)
- fixed (byte* count = spec.Count)
- fixed (byte* values = spec.Values)
- {
- huffman[len++] = headers[i];
-
- for (int c = 0; c < spec.Count.Length; c++)
- {
- huffman[len++] = count[c];
- }
-
- for (int v = 0; v < spec.Values.Length; v++)
- {
- huffman[len++] = values[v];
- }
- }
-
- this.outputStream.Write(this.huffmanBuffer, 0, len);
- }
- }
-
///
/// Writes the StartOfScan marker.
///
@@ -819,6 +740,7 @@ namespace ImageSharp.Formats
private void WriteStartOfScan(PixelAccessor pixels)
where TColor : struct, IPackedPixel, IEquatable
{
+ // TODO: This method should be the entry point for a JpegEncoderScanImpl struct
// TODO: We should allow grayscale writing.
this.outputStream.Write(SosHeaderYCbCr, 0, SosHeaderYCbCr.Length);
@@ -836,67 +758,6 @@ namespace ImageSharp.Formats
this.Emit(0x7f, 7);
}
- ///
- /// Encodes the image with no subsampling.
- ///
- /// The pixel format.
- /// The pixel accessor providing access to the image pixels.
- private void Encode444(PixelAccessor pixels)
- where TColor : struct, IPackedPixel, IEquatable
- {
- // TODO: Need a JpegEncoderCoreCore class or struct to hold all this mess:
- Block8x8F b = default(Block8x8F);
- Block8x8F cb = default(Block8x8F);
- Block8x8F cr = default(Block8x8F);
-
- Block8x8F temp1 = default(Block8x8F);
- Block8x8F temp2 = default(Block8x8F);
-
- Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
- Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
-
- UnzigData unzig = UnzigData.Create();
-
- // ReSharper disable once InconsistentNaming
- float prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
-
- using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.XYZ, true))
- {
- for (int y = 0; y < pixels.Height; y += 8)
- {
- for (int x = 0; x < pixels.Width; x += 8)
- {
- ToYCbCr(pixels, x, y, &b, &cb, &cr, rgbBytes);
-
- prevDCY = this.WriteBlock(
- QuantIndex.Luminance,
- prevDCY,
- &b,
- &temp1,
- &temp2,
- &onStackLuminanceQuantTable,
- unzig.Data);
- prevDCCb = this.WriteBlock(
- QuantIndex.Chrominance,
- prevDCCb,
- &cb,
- &temp1,
- &temp2,
- &onStackChrominanceQuantTable,
- unzig.Data);
- prevDCCr = this.WriteBlock(
- QuantIndex.Chrominance,
- prevDCCr,
- &cr,
- &temp1,
- &temp2,
- &onStackChrominanceQuantTable,
- unzig.Data);
- }
- }
- }
- }
-
#pragma warning disable SA1201 // MethodShouldNotFollowAStruct
///
@@ -916,7 +777,7 @@ namespace ImageSharp.Formats
private void Encode420(PixelAccessor pixels)
where TColor : struct, IPackedPixel, IEquatable
{
- // TODO: Need a JpegEncoderCoreCore class or struct to hold all this mess:
+ // TODO: Need a JpegEncoderScanImpl struct to encapsulate all this mess:
Block8x8F b = default(Block8x8F);
BlockQuad cb = default(BlockQuad);
@@ -997,81 +858,5 @@ namespace ImageSharp.Formats
this.buffer[3] = (byte)(length & 0xff);
this.outputStream.Write(this.buffer, 0, 4);
}
-
- ///
- /// The Huffman encoding specifications.
- ///
- private struct HuffmanSpec
- {
- ///
- /// Gets count[i] - The number of codes of length i bits.
- ///
- public readonly byte[] Count;
-
- ///
- /// Gets value[i] - The decoded value of the codeword at the given index.
- ///
- public readonly byte[] Values;
-
- ///
- /// Initializes a new instance of the struct.
- ///
- /// The number of codes.
- /// The decoded values.
- public HuffmanSpec(byte[] count, byte[] values)
- {
- this.Count = count;
- this.Values = values;
- }
- }
-
- ///
- /// A compiled look-up table representation of a huffmanSpec.
- /// Each value maps to a uint32 of which the 8 most significant bits hold the
- /// codeword size in bits and the 24 least significant bits hold the codeword.
- /// The maximum codeword size is 16 bits.
- ///
- private class HuffmanLut
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The encoding specifications.
- public HuffmanLut(HuffmanSpec spec)
- {
- int maxValue = 0;
-
- foreach (byte v in spec.Values)
- {
- if (v > maxValue)
- {
- maxValue = v;
- }
- }
-
- this.Values = new uint[maxValue + 1];
-
- int code = 0;
- int k = 0;
-
- for (int i = 0; i < spec.Count.Length; i++)
- {
- int bits = (i + 1) << 24;
- for (int j = 0; j < spec.Count[i]; j++)
- {
- this.Values[spec.Values[k]] = (uint)(bits | code);
- code++;
- k++;
- }
-
- code <<= 1;
- }
- }
-
- ///
- /// Gets the collection of huffman values.
- ///
- public uint[] Values { get; }
- }
}
-}
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
index 48d5454f6e..a84c64abae 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
@@ -72,7 +72,7 @@ namespace ImageSharp.Tests
//PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb;
[Theory(
- //Skip = "Benchmark, enable manually!"
+ Skip = "Benchmark, enable manually!"
)]
[WithFileCollection(nameof(BenchmarkFiles), BenchmarkPixels, JpegSubsample.Ratio420, 75)]
[WithFileCollection(nameof(BenchmarkFiles), BenchmarkPixels, JpegSubsample.Ratio444, 75)]
diff --git a/tests/ImageSharp.Tests46/Formats/Jpg/ReferenceImplementationsTests.cs b/tests/ImageSharp.Tests46/Formats/Jpg/ReferenceImplementationsTests.cs
deleted file mode 100644
index e30baddfc8..0000000000
--- a/tests/ImageSharp.Tests46/Formats/Jpg/ReferenceImplementationsTests.cs
+++ /dev/null
@@ -1,145 +0,0 @@
-// ReSharper disable InconsistentNaming
-namespace ImageSharp.Tests
-{
- using System.Numerics;
- using ImageSharp.Formats;
-
- using Xunit;
- using Xunit.Abstractions;
-
- public class ReferenceImplementationsTests : UtilityTestClassBase
- {
- public ReferenceImplementationsTests(ITestOutputHelper output)
- : base(output)
- {
- }
-
-
- [Theory]
- [InlineData(42)]
- [InlineData(1)]
- [InlineData(2)]
- public void Idct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed)
- {
- MutableSpan intData = Create8x8RandomIntData(-200, 200, seed);
- MutableSpan floatSrc = intData.ConvertToFloat32MutableSpan();
-
- ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(intData);
-
- MutableSpan dest = new MutableSpan(64);
- MutableSpan temp = new MutableSpan(64);
-
- ReferenceImplementations.iDCT2D_llm(floatSrc, dest, temp);
-
- for (int i = 0; i < 64; i++)
- {
- float expected = intData[i];
- float actual = dest[i];
-
- Assert.Equal(expected, actual, new ApproximateFloatComparer(1f));
- }
- }
-
- [Theory]
- [InlineData(42, 0)]
- [InlineData(1, 0)]
- [InlineData(2, 0)]
- public void IntegerDCT_ForwardThenInverse(int seed, int startAt)
- {
- MutableSpan original = Create8x8RandomIntData(-200, 200, seed);
-
- var block = original.AddScalarToAllValues(128);
-
- ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(block);
-
- for (int i = 0; i < 64; i++)
- {
- block[i] /= 8;
- }
-
- ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(block);
-
- for (int i = startAt; i < 64; i++)
- {
- float expected = original[i];
- float actual = (float)block[i];
-
- Assert.Equal(expected, actual, new ApproximateFloatComparer(3f));
- }
-
- }
-
- [Theory]
- [InlineData(42, 0)]
- [InlineData(1, 0)]
- [InlineData(2, 0)]
- public void FloatingPointDCT_ReferenceImplementation_ForwardThenInverse(int seed, int startAt)
- {
- var data = Create8x8RandomIntData(-200, 200, seed);
- MutableSpan src = new MutableSpan(data).ConvertToFloat32MutableSpan();
- MutableSpan dest = new MutableSpan(64);
- MutableSpan temp = new MutableSpan(64);
-
- ReferenceImplementations.fDCT2D_llm(src, dest, temp, true);
- ReferenceImplementations.iDCT2D_llm(dest, src, temp);
-
- for (int i = startAt; i < 64; i++)
- {
- float expected = data[i];
- float actual = (float)src[i];
-
- Assert.Equal(expected, actual, new ApproximateFloatComparer(2f));
- }
- }
-
- [Fact]
- public void HowMuchIsTheFish()
- {
- Output.WriteLine(Vector.Count.ToString());
- }
-
- [Theory]
- [InlineData(42)]
- [InlineData(1)]
- [InlineData(2)]
- public void Fdct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed)
- {
- MutableSpan intData = Create8x8RandomIntData(-200, 200, seed);
- MutableSpan floatSrc = intData.ConvertToFloat32MutableSpan();
-
- ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(intData);
-
- MutableSpan dest = new MutableSpan(64);
- MutableSpan temp = new MutableSpan(64);
-
- ReferenceImplementations.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: true);
-
- for (int i = 0; i < 64; i++)
- {
- float expected = intData[i];
- float actual = dest[i];
-
- 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
diff --git a/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj b/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj
index 2f03fa24c2..d53ee3d9c7 100644
--- a/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj
+++ b/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj
@@ -82,6 +82,9 @@
Formats\Jpg\ReferenceImplementations.cs
+
+ Formats\Jpg\ReferenceImplementationsTests.cs
+
Formats\Jpg\UtilityTestClassBase.cs
@@ -157,7 +160,6 @@
TestUtilities\TestUtilityExtensions.cs
-