diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs
index 44b39dfd71..6e17762c72 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs
@@ -31,6 +31,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
///
public static readonly HuffmanLut[] TheHuffmanLut = new HuffmanLut[4];
+ public static readonly HuffmanLut[] DcHuffmanLut = new HuffmanLut[2];
+ public static readonly HuffmanLut[] AcHuffmanLut = new HuffmanLut[2];
+
///
/// Initializes static members of the struct.
///
@@ -41,6 +44,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
TheHuffmanLut[i] = new HuffmanLut(HuffmanSpec.TheHuffmanSpecs[i]);
}
+
+ // TODO: REWRITE THIS
+ DcHuffmanLut[0] = new HuffmanLut(HuffmanSpec.TheHuffmanSpecs[0]);
+ DcHuffmanLut[1] = new HuffmanLut(HuffmanSpec.TheHuffmanSpecs[2]);
+ AcHuffmanLut[0] = new HuffmanLut(HuffmanSpec.TheHuffmanSpecs[1]);
+ AcHuffmanLut[1] = new HuffmanLut(HuffmanSpec.TheHuffmanSpecs[3]);
}
///
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
index bc765742e9..9803bcf2a4 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
@@ -132,8 +132,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
where TPixel : unmanaged, IPixel
{
// DEBUG INITIALIZATION SETUP
- this.huffmanTables = HuffmanLut.TheHuffmanLut;
-
frame.Init(1, 1);
frame.AllocateComponents(fullScan: false);
@@ -161,8 +159,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
JpegComponent component = frame.Components[k];
- ref HuffmanLut dcHuffmanTable = ref this.huffmanTables[component.DcTableId];
- ref HuffmanLut acHuffmanTable = ref this.huffmanTables[component.AcTableId];
+ ref HuffmanLut dcHuffmanTable = ref HuffmanLut.DcHuffmanLut[component.DcTableId];
+ ref HuffmanLut acHuffmanTable = ref HuffmanLut.AcHuffmanLut[component.AcTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
index 7323a30baf..87fd218eb8 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
@@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public JpegComponentConfig[] Components { get; }
- public JpegFrameConfig PopulateComponent(int index, int id, int hsf, int vsf, int quantIndex, int dcIndex, int acIndex)
+ public JpegFrameConfig PopulateComponent(int index, byte id, int hsf, int vsf, int quantIndex, int dcIndex, int acIndex)
{
this.Components[index] = new JpegComponentConfig
{
@@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public class JpegComponentConfig
{
- public int Id { get; set; }
+ public byte Id { get; set; }
public int HorizontalSampleFactor { get; set; }
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index 065d9c6019..082ab83ccf 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -89,7 +89,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Compute number of components based on color type in options.
int componentCount = (this.colorType == JpegColorType.Luminance) ? 1 : 3;
- ReadOnlySpan componentIds = this.GetComponentIds();
// TODO: Right now encoder writes both quantization tables for grayscale images - we shouldn't do that
// Initialize the quantization tables.
@@ -117,13 +116,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.WriteDefineQuantizationTables(ref luminanceQuantTable, ref chrominanceQuantTable);
// Write the image dimensions.
- this.WriteStartOfFrame(image.Width, image.Height, componentCount, componentIds);
+ this.WriteStartOfFrame(image.Width, image.Height, this.frameConfig.Components);
// Write the Huffman tables.
this.WriteDefineHuffmanTables(componentCount);
// Write the scan header.
- this.WriteStartOfScan(componentCount, componentIds);
+ this.WriteStartOfScan(componentCount, this.frameConfig.Components);
var frame = new Components.Encoder.JpegFrame(this.frameConfig, Configuration.Default.MemoryAllocator, image, GetTargetColorSpace(this.frameConfig.ColorType));
var quantTables = new Block8x8F[] { luminanceQuantTable, chrominanceQuantTable };
@@ -200,15 +199,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
|| colorType == JpegColorType.Luminance
|| colorType == JpegColorType.Rgb;
- ///
- /// Gets the component ids.
- /// For color space RGB this will be RGB as ASCII, otherwise 1, 2, 3.
- ///
- /// The component Ids.
- private ReadOnlySpan GetComponentIds() => this.colorType == JpegColorType.Rgb
- ? new ReadOnlySpan(new byte[] { 82, 71, 66 })
- : new ReadOnlySpan(new byte[] { 1, 2, 3 });
-
///
/// Writes data to "Define Quantization Tables" block for QuantIndex.
///
@@ -646,91 +636,37 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The height of the image.
/// The number of components in a pixel.
/// The component Id's.
- private void WriteStartOfFrame(int width, int height, int componentCount, ReadOnlySpan componentIds)
+ private void WriteStartOfFrame(int width, int height, JpegComponentConfig[] components)
{
- // This uses a C#'s compiler optimization that refers to the static data segment of the assembly,
- // and doesn't incur any allocation at all.
- // "default" to 4:2:0
- ReadOnlySpan subsamples = new byte[]
- {
- 0x22,
- 0x11,
- 0x11
- };
-
- ReadOnlySpan chroma = new byte[]
- {
- 0x00,
- 0x01,
- 0x01
- };
-
- if (this.colorType == JpegColorType.Luminance)
- {
- subsamples = new byte[]
- {
- 0x11,
- 0x00,
- 0x00
- };
- }
- else
- {
- switch (this.colorType)
- {
- case JpegColorType.YCbCrRatio444:
- case JpegColorType.Rgb:
- subsamples = new byte[]
- {
- 0x11,
- 0x11,
- 0x11
- };
-
- if (this.colorType == JpegColorType.Rgb)
- {
- chroma = new byte[]
- {
- 0x00,
- 0x00,
- 0x00
- };
- }
-
- break;
- case JpegColorType.YCbCrRatio420:
- subsamples = new byte[]
- {
- 0x22,
- 0x11,
- 0x11
- };
- break;
- }
- }
-
// Length (high byte, low byte), 8 + components * 3.
- int markerlen = 8 + (3 * componentCount);
+ int markerlen = 8 + (3 * components.Length);
this.WriteMarkerHeader(JpegConstants.Markers.SOF0, markerlen);
this.buffer[0] = 8; // Data Precision. 8 for now, 12 and 16 bit jpegs not supported
this.buffer[1] = (byte)(height >> 8);
this.buffer[2] = (byte)(height & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported
this.buffer[3] = (byte)(width >> 8);
this.buffer[4] = (byte)(width & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported
- this.buffer[5] = (byte)componentCount;
+ this.buffer[5] = (byte)components.Length;
- for (int i = 0; i < componentCount; i++)
+ // Components data
+ for (int i = 0; i < components.Length; i++)
{
int i3 = 3 * i;
-
- // Component ID.
Span bufferSpan = this.buffer.AsSpan(i3 + 6, 3);
- bufferSpan[2] = chroma[i];
- bufferSpan[1] = subsamples[i];
- bufferSpan[0] = componentIds[i];
+
+ // Quantization table selector
+ bufferSpan[2] = (byte)components[i].QuantizatioTableIndex;
+
+ // Sampling factors
+ // 4 bits
+ int samplingFactors = components[i].HorizontalSampleFactor | (components[i].VerticalSampleFactor << 4);
+ bufferSpan[1] = (byte)samplingFactors;
+
+ // Id
+ bufferSpan[0] = components[i].Id;
}
- this.outputStream.Write(this.buffer, 0, (3 * (componentCount - 1)) + 9);
+ this.outputStream.Write(this.buffer, 0, (3 * (components.Length - 1)) + 9);
}
///
@@ -738,28 +674,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The number of components in a pixel.
/// The componentId's.
- private void WriteStartOfScan(int componentCount, ReadOnlySpan componentIds)
+ private void WriteStartOfScan(int componentCount, JpegComponentConfig[] components)
{
- // This uses a C#'s compiler optimization that refers to the static data segment of the assembly,
- // and doesn't incur any allocation at all.
- ReadOnlySpan huffmanId = new byte[]
- {
- 0x00,
- 0x11,
- 0x11
- };
-
- // Use the same DC/AC tables for all channels for RGB.
- if (this.colorType == JpegColorType.Rgb)
- {
- huffmanId = new byte[]
- {
- 0x00,
- 0x00,
- 0x00
- };
- }
-
// Write the SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes:
// - the marker length "\x00\x0c",
// - the number of components "\x03",
@@ -777,11 +693,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.buffer[2] = 0x00;
this.buffer[3] = (byte)sosSize;
this.buffer[4] = (byte)componentCount; // Number of components in a scan
+
+ // Components data
for (int i = 0; i < componentCount; i++)
{
int i2 = 2 * i;
- this.buffer[i2 + 5] = componentIds[i]; // Component Id
- this.buffer[i2 + 6] = huffmanId[i]; // DC/AC Huffman table
+
+ // Id
+ this.buffer[i2 + 5] = components[i].Id;
+
+ // Table selectors
+ int tableSelectors = (components[i].dcTableSelector << 4) | (components[i].acTableSelector);
+ this.buffer[i2 + 6] = (byte)tableSelectors;
}
this.buffer[sosSize - 1] = 0x00; // Ss - Start of spectral selection.