diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index f1dd7f6bf1..019be629bb 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -92,67 +92,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.colorType = options.ColorType;
}
- ///
- /// Gets the counts the number of bits needed to hold an integer.
- ///
- // The C# compiler emits this as a compile-time constant embedded in the PE file.
- // This is effectively compiled down to: return new ReadOnlySpan(&data, length)
- // More details can be found: https://github.com/dotnet/roslyn/pull/24621
- private static ReadOnlySpan BitCountLut => new byte[]
- {
- 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8,
- };
-
- ///
- /// Gets 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.
- ///
- // The C# compiler emits this as a compile-time constant embedded in the PE file.
- // This is effectively compiled down to: return new ReadOnlySpan(&data, length)
- // More details can be found: https://github.com/dotnet/roslyn/pull/24621
- private static ReadOnlySpan UnscaledQuant_Luminance => new byte[]
- {
- // 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,
- };
-
- ///
- /// Gets 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.
- ///
- // The C# compiler emits this as a compile-time constant embedded in the PE file.
- // This is effectively compiled down to: return new ReadOnlySpan(&data, length)
- // More details can be found: https://github.com/dotnet/roslyn/pull/24621
- private static ReadOnlySpan UnscaledQuant_Chrominance => new byte[]
- {
- // 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,
- };
-
///
/// Encode writes the image to the jpeg baseline format with the given options.
///
@@ -228,248 +167,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
}
- ///
- /// Initializes quantization table.
- ///
- /// The quantization index.
- /// The scaling factor.
- /// The quantization table.
- private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant)
- {
- DebugGuard.MustBeBetweenOrEqualTo(i, 0, 1, nameof(i));
- ReadOnlySpan unscaledQuant = (i == 0) ? UnscaledQuant_Luminance : UnscaledQuant_Chrominance;
-
- for (int j = 0; j < Block8x8F.Size; j++)
- {
- int x = unscaledQuant[j];
- x = ((x * scale) + 50) / 100;
- if (x < 1)
- {
- x = 1;
- }
-
- if (x > 255)
- {
- x = 255;
- }
-
- quant[j] = x;
- }
- }
-
- ///
- /// Emits the least significant count of bits of bits to the bit-stream.
- /// The precondition is bits
- ///
- /// < 1<<nBits && nBits <= 16
- ///
- /// .
- ///
- /// The packed bits.
- /// The number of bits
- /// The reference to the emitBuffer.
- [MethodImpl(InliningOptions.ShortMethod)]
- private void Emit(uint bits, uint count, ref byte emitBufferBase)
- {
- count += this.bitCount;
- bits <<= (int)(32 - count);
- bits |= this.accumulatedBits;
-
- // Only write if more than 8 bits.
- if (count >= 8)
- {
- // Track length
- int len = 0;
- while (count >= 8)
- {
- byte b = (byte)(bits >> 24);
- Unsafe.Add(ref emitBufferBase, len++) = b;
- if (b == byte.MaxValue)
- {
- Unsafe.Add(ref emitBufferBase, len++) = byte.MinValue;
- }
-
- bits <<= 8;
- count -= 8;
- }
-
- if (len > 0)
- {
- this.outputStream.Write(this.emitBuffer, 0, len);
- }
- }
-
- this.accumulatedBits = bits;
- this.bitCount = count;
- }
-
- ///
- /// Emits the given value with the given Huffman encoder.
- ///
- /// The index of the Huffman encoder
- /// The value to encode.
- /// The reference to the emit buffer.
- [MethodImpl(InliningOptions.ShortMethod)]
- private void EmitHuff(HuffIndex index, int value, ref byte emitBufferBase)
- {
- uint x = HuffmanLut.TheHuffmanLut[(int)index].Values[value];
- this.Emit(x & ((1 << 24) - 1), x >> 24, ref emitBufferBase);
- }
-
- ///
- /// Emits a run of runLength copies of value encoded with the given Huffman encoder.
- ///
- /// The index of the Huffman encoder
- /// The number of copies to encode.
- /// The value to encode.
- /// The reference to the emit buffer.
- [MethodImpl(InliningOptions.ShortMethod)]
- private void EmitHuffRLE(HuffIndex index, int runLength, int value, ref byte emitBufferBase)
- {
- int a = value;
- int b = value;
- if (a < 0)
- {
- a = -value;
- b = value - 1;
- }
-
- uint bt;
- if (a < 0x100)
- {
- bt = BitCountLut[a];
- }
- else
- {
- bt = 8 + (uint)BitCountLut[a >> 8];
- }
-
- this.EmitHuff(index, (int)((uint)(runLength << 4) | bt), ref emitBufferBase);
- if (bt > 0)
- {
- this.Emit((uint)b & (uint)((1 << ((int)bt)) - 1), bt, ref emitBufferBase);
- }
- }
-
- ///
- /// Encodes the image with no subsampling.
- ///
- /// The pixel format.
- /// The pixel accessor providing access to the image pixels.
- /// The token to monitor for cancellation.
- /// The reference to the emit buffer.
- private void Encode444(Image pixels, CancellationToken cancellationToken, ref byte emitBufferBase)
- where TPixel : unmanaged, IPixel
- {
- // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
- // (Partially done with YCbCrForwardConverter)
- Block8x8F temp1 = default;
- Block8x8F temp2 = default;
-
- Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
- Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
-
- var unzig = ZigZag.CreateUnzigTable();
-
- // ReSharper disable once InconsistentNaming
- int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
-
- var pixelConverter = YCbCrForwardConverter.Create();
- ImageFrame frame = pixels.Frames.RootFrame;
- Buffer2D pixelBuffer = frame.PixelBuffer;
- RowOctet currentRows = default;
-
- for (int y = 0; y < pixels.Height; y += 8)
- {
- cancellationToken.ThrowIfCancellationRequested();
- currentRows.Update(pixelBuffer, y);
-
- for (int x = 0; x < pixels.Width; x += 8)
- {
- pixelConverter.Convert(frame, x, y, ref currentRows);
-
- prevDCY = this.WriteBlock(
- QuantIndex.Luminance,
- prevDCY,
- ref pixelConverter.Y,
- ref temp1,
- ref temp2,
- ref onStackLuminanceQuantTable,
- ref unzig,
- ref emitBufferBase);
-
- prevDCCb = this.WriteBlock(
- QuantIndex.Chrominance,
- prevDCCb,
- ref pixelConverter.Cb,
- ref temp1,
- ref temp2,
- ref onStackChrominanceQuantTable,
- ref unzig,
- ref emitBufferBase);
-
- prevDCCr = this.WriteBlock(
- QuantIndex.Chrominance,
- prevDCCr,
- ref pixelConverter.Cr,
- ref temp1,
- ref temp2,
- ref onStackChrominanceQuantTable,
- ref unzig,
- ref emitBufferBase);
- }
- }
- }
-
- ///
- /// Encodes the image with no chroma, just luminance.
- ///
- /// The pixel format.
- /// The pixel accessor providing access to the image pixels.
- /// The token to monitor for cancellation.
- /// The reference to the emit buffer.
- private void EncodeGrayscale(Image pixels, CancellationToken cancellationToken, ref byte emitBufferBase)
- where TPixel : unmanaged, IPixel
- {
- // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
- // (Partially done with YCbCrForwardConverter)
- Block8x8F temp1 = default;
- Block8x8F temp2 = default;
-
- Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
-
- var unzig = ZigZag.CreateUnzigTable();
-
- // ReSharper disable once InconsistentNaming
- int prevDCY = 0;
-
- var pixelConverter = LuminanceForwardConverter.Create();
- ImageFrame frame = pixels.Frames.RootFrame;
- Buffer2D pixelBuffer = frame.PixelBuffer;
- RowOctet currentRows = default;
-
- for (int y = 0; y < pixels.Height; y += 8)
- {
- cancellationToken.ThrowIfCancellationRequested();
- currentRows.Update(pixelBuffer, y);
-
- for (int x = 0; x < pixels.Width; x += 8)
- {
- pixelConverter.Convert(frame, x, y, ref currentRows);
-
- prevDCY = this.WriteBlock(
- QuantIndex.Luminance,
- prevDCY,
- ref pixelConverter.Y,
- ref temp1,
- ref temp2,
- ref onStackLuminanceQuantTable,
- ref unzig,
- ref emitBufferBase);
- }
- }
- }
-
///
/// Writes the application header containing the JFIF identifier plus extra data.
///
@@ -519,72 +216,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.outputStream.Write(this.buffer, 0, 20);
}
- ///
- /// Writes a block of pixel data using the given quantization table,
- /// returning the post-quantized DC value of the DCT-transformed block.
- /// The block is in natural (not zig-zag) order.
- ///
- /// The quantization table index.
- /// The previous DC value.
- /// Source block
- /// Temporal block to be used as FDCT Destination
- /// Temporal block 2
- /// Quantization table
- /// The 8x8 Unzig block.
- /// The reference to the emit buffer.
- /// The .
- private int WriteBlock(
- QuantIndex index,
- int prevDC,
- ref Block8x8F src,
- ref Block8x8F tempDest1,
- ref Block8x8F tempDest2,
- ref Block8x8F quant,
- ref ZigZag unZig,
- ref byte emitBufferBase)
- {
- FastFloatingPointDCT.TransformFDCT(ref src, ref tempDest1, ref tempDest2);
-
- Block8x8F.Quantize(ref tempDest1, ref tempDest2, ref quant, ref unZig);
-
- int dc = (int)tempDest2[0];
-
- // Emit the DC delta.
- this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC, ref emitBufferBase);
-
- // Emit the AC components.
- var h = (HuffIndex)((2 * (int)index) + 1);
- int runLength = 0;
-
- for (int zig = 1; zig < Block8x8F.Size; zig++)
- {
- int ac = (int)tempDest2[zig];
-
- if (ac == 0)
- {
- runLength++;
- }
- else
- {
- while (runLength > 15)
- {
- this.EmitHuff(h, 0xf0, ref emitBufferBase);
- runLength -= 16;
- }
-
- this.EmitHuffRLE(h, runLength, ac, ref emitBufferBase);
- runLength = 0;
- }
- }
-
- if (runLength > 0)
- {
- this.EmitHuff(h, 0x00, ref emitBufferBase);
- }
-
- return dc;
- }
-
///
/// Writes the Define Huffman Table marker and tables.
///
@@ -1017,110 +648,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
scanEncoder.WriteStartOfScan(image, this.colorType, this.subsample, cancellationToken);
- //ref byte emitBufferBase = ref MemoryMarshal.GetReference(this.emitBuffer);
- //if (this.colorType == JpegColorType.Luminance)
- //{
- // scanEncoder.EncodeGrayscale(image, cancellationToken);
- //}
- //else
- //{
- // switch (this.subsample)
- // {
- // case JpegSubsample.Ratio444:
- // scanEncoder.Encode444(image, cancellationToken);
- // break;
- // case JpegSubsample.Ratio420:
- // scanEncoder.Encode420(image, cancellationToken);
- // break;
- // }
- //}
-
- //// Pad the last byte with 1's.
- //this.Emit(0x7f, 7, ref emitBufferBase);
- }
-
- ///
- /// Encodes the image with subsampling. The Cb and Cr components are each subsampled
- /// at a factor of 2 both horizontally and vertically.
- ///
- /// The pixel format.
- /// The pixel accessor providing access to the image pixels.
- /// The token to monitor for cancellation.
- /// The reference to the emit buffer.
- private void Encode420(Image pixels, CancellationToken cancellationToken, ref byte emitBufferBase)
- where TPixel : unmanaged, IPixel
- {
- // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
- Block8x8F b = default;
- Span cb = stackalloc Block8x8F[4];
- Span cr = stackalloc Block8x8F[4];
-
- Block8x8F temp1 = default;
- Block8x8F temp2 = default;
-
- Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
- Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
-
- var unzig = ZigZag.CreateUnzigTable();
-
- var pixelConverter = YCbCrForwardConverter.Create();
-
- // ReSharper disable once InconsistentNaming
- int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
- ImageFrame frame = pixels.Frames.RootFrame;
- Buffer2D pixelBuffer = frame.PixelBuffer;
- RowOctet currentRows = default;
-
- for (int y = 0; y < pixels.Height; y += 16)
- {
- cancellationToken.ThrowIfCancellationRequested();
- for (int x = 0; x < pixels.Width; x += 16)
- {
- for (int i = 0; i < 4; i++)
- {
- int xOff = (i & 1) * 8;
- int yOff = (i & 2) * 4;
-
- currentRows.Update(pixelBuffer, y + yOff);
- pixelConverter.Convert(frame, x + xOff, y + yOff, ref currentRows);
-
- cb[i] = pixelConverter.Cb;
- cr[i] = pixelConverter.Cr;
-
- prevDCY = this.WriteBlock(
- QuantIndex.Luminance,
- prevDCY,
- ref pixelConverter.Y,
- ref temp1,
- ref temp2,
- ref onStackLuminanceQuantTable,
- ref unzig,
- ref emitBufferBase);
- }
-
- Block8x8F.Scale16X16To8X8(ref b, cb);
- prevDCCb = this.WriteBlock(
- QuantIndex.Chrominance,
- prevDCCb,
- ref b,
- ref temp1,
- ref temp2,
- ref onStackChrominanceQuantTable,
- ref unzig,
- ref emitBufferBase);
-
- Block8x8F.Scale16X16To8X8(ref b, cr);
- prevDCCr = this.WriteBlock(
- QuantIndex.Chrominance,
- prevDCCr,
- ref b,
- ref temp1,
- ref temp2,
- ref onStackChrominanceQuantTable,
- ref unzig,
- ref emitBufferBase);
- }
- }
}
///