From 4ff29844febdc5e59c0fbd33461741f197d293cf Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Fri, 1 Oct 2021 22:35:36 +0300 Subject: [PATCH] Docs --- .../Components/Encoder/HuffmanScanEncoder.cs | 120 +++++++++++++----- 1 file changed, 90 insertions(+), 30 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs index 35e0e26485..bbdd3220f0 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs @@ -65,6 +65,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// Yields codewords by index consisting of [run length | bitsize]. private HuffmanLut[] huffmanTables; + /// + /// Emitted bits 'micro buffer' before being transferred to the . + /// + private uint accumulatedBits; + /// /// Buffer for temporal storage of huffman rle encoding bit data. /// @@ -82,18 +87,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// private readonly byte[] streamWriteBuffer; - private int emitWriteIndex; - - /// - /// Emitted bits 'micro buffer' before being transferred to the . - /// - private uint accumulatedBits; - /// /// Number of jagged bits stored in /// private int bitCount; + private int emitWriteIndex; + private Block8x8 tempBlock; /// @@ -101,9 +101,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// private readonly Stream target; - public HuffmanScanEncoder(int componentCount, Stream outputStream) + /// + /// Initializes a new instance of the class. + /// + /// Amount of encoded 8x8 blocks per single jpeg macroblock. + /// Output stream for saving encoded data. + public HuffmanScanEncoder(int blocksPerCodingUnit, Stream outputStream) { - int emitBufferByteLength = MaxBytesPerBlock * componentCount; + int emitBufferByteLength = MaxBytesPerBlock * blocksPerCodingUnit; this.emitBuffer = new uint[emitBufferByteLength / sizeof(uint)]; this.emitWriteIndex = this.emitBuffer.Length; @@ -112,7 +117,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder this.target = outputStream; } - private bool IsFlushNeeded + /// + /// Gets a value indicating whether is full + /// and must be flushed using + /// before encoding next 8x8 coding block. + /// + private bool IsStreamFlushNeeded { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this.emitWriteIndex < (uint)this.emitBuffer.Length / 2; @@ -174,7 +184,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder ref pixelConverter.Cr, ref chrominanceQuantTable); - if (this.IsFlushNeeded) + if (this.IsStreamFlushNeeded) { this.FlushToStream(); } @@ -249,7 +259,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder ref pixelConverter.Cr, ref chrominanceQuantTable); - if (this.IsFlushNeeded) + if (this.IsStreamFlushNeeded) { this.FlushToStream(); } @@ -300,7 +310,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder ref pixelConverter.Y, ref luminanceQuantTable); - if (this.IsFlushNeeded) + if (this.IsStreamFlushNeeded) { this.FlushToStream(); } @@ -364,7 +374,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder ref pixelConverter.B, ref luminanceQuantTable); - if (this.IsFlushNeeded) + if (this.IsStreamFlushNeeded) { this.FlushToStream(); } @@ -447,15 +457,48 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder } /// - /// Emits the least significant count of bits to the stream write buffer. - /// The precondition is bits - /// - /// < 1<<nBits && nBits <= 16 - /// - /// . + /// Emits the most significant count of bits to the buffer. /// - /// The packed bits. - /// The number of bits + /// + /// + /// Supports up to 32 count of bits but, generally speaking, jpeg + /// standard assures that there won't be more than 16 bits per single + /// value. + /// + /// + /// Emitting algorithm uses 3 intermediate buffers for caching before + /// writing to the stream: + /// + /// + /// uint32 + /// + /// Bit buffer. Encoded spectral values can occupy up to 16 bits, bits + /// are assembled to whole bytes via this intermediate buffer. + /// + /// + /// + /// uint32[] + /// + /// Assembled bytes from uint32 buffer are saved into this buffer. + /// uint32 buffer values are saved using indices from the last to the first. + /// As bytes are saved to the memory as 4-byte packages endianness matters: + /// Jpeg stream is big-endian, indexing buffer bytes from the last index to the + /// first eliminates all operations to extract separate bytes. This only works for + /// little-endian machines (there are no known examples of big-endian users atm). + /// For big-endians this approach is slower due to the separate byte extraction. + /// + /// + /// + /// byte[] + /// + /// Byte buffer used only during method. + /// + /// + /// + /// + /// + /// Bits to emit, must be shifted to the left. + /// Bits count stored in the bits parameter. [MethodImpl(InliningOptions.ShortMethod)] private void Emit(uint bits, int count) { @@ -475,10 +518,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder } /// - /// Emits the given value with the given Huffman encoder. + /// Emits the given value with the given Huffman table. /// - /// Compiled Huffman spec values. - /// The value to encode. + /// Huffman table. + /// Value to encode. [MethodImpl(InliningOptions.ShortMethod)] private void EmitHuff(int[] table, int value) { @@ -489,9 +532,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// /// Emits given value via huffman rle encoding. /// - /// Compiled Huffman spec values. + /// Huffman table. /// The number of preceding zeroes, preshifted by 4 to the left. - /// The value to encode. + /// Value to encode. [MethodImpl(InliningOptions.ShortMethod)] private void EmitHuffRLE(int[] table, int runLength, int value) { @@ -555,11 +598,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder } /// - /// Flushes cached bytes to the ouput stream respecting stuff bytes. + /// General method for flushing cached spectral data bytes to + /// the ouput stream respecting stuff bytes. /// /// - /// Bytes cached via are stored in 4-bytes blocks which makes - /// this method endianness dependent. + /// Bytes cached via are stored in 4-bytes blocks + /// which makes this method endianness dependent. /// [MethodImpl(InliningOptions.ShortMethod)] private void FlushToStream(int endIndex) @@ -623,12 +667,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder this.target.Write(this.streamWriteBuffer, 0, writeIdx); } + /// + /// Flushes spectral data bytes after encoding all channel blocks + /// in a single jpeg macroblock using . + /// + /// + /// This must be called only if is true + /// only during the macroblocks encoding routine. + /// private void FlushToStream() { this.FlushToStream(this.emitWriteIndex * 4); this.emitWriteIndex = this.emitBuffer.Length; } + /// + /// Flushes final cached bits to the stream padding 1's to + /// complement full bytes. + /// + /// + /// This must be called only once at the end of the encoding routine. + /// check is not needed. + /// [MethodImpl(InliningOptions.ShortMethod)] private void FlushRemainingBytes() {