@@ -87,6 +87,8 @@ internal class HuffmanScanEncoder
8787 /// </remarks>
8888 private readonly byte [ ] streamWriteBuffer ;
8989
90+ private readonly int restartInterval ;
91+
9092 /// <summary>
9193 /// Number of jagged bits stored in <see cref="accumulatedBits"/>
9294 /// </summary>
@@ -103,13 +105,16 @@ internal class HuffmanScanEncoder
103105 /// Initializes a new instance of the <see cref="HuffmanScanEncoder"/> class.
104106 /// </summary>
105107 /// <param name="blocksPerCodingUnit">Amount of encoded 8x8 blocks per single jpeg macroblock.</param>
108+ /// <param name="restartInterval">Numbers of MCUs between restart markers.</param>
106109 /// <param name="outputStream">Output stream for saving encoded data.</param>
107- public HuffmanScanEncoder ( int blocksPerCodingUnit , Stream outputStream )
110+ public HuffmanScanEncoder ( int blocksPerCodingUnit , int restartInterval , Stream outputStream )
108111 {
109112 int emitBufferByteLength = MaxBytesPerBlock * blocksPerCodingUnit ;
110113 this . emitBuffer = new uint [ emitBufferByteLength / sizeof ( uint ) ] ;
111114 this . emitWriteIndex = this . emitBuffer . Length ;
112115
116+ this . restartInterval = restartInterval ;
117+
113118 this . streamWriteBuffer = new byte [ emitBufferByteLength * OutputBufferLengthMultiplier ] ;
114119
115120 this . target = outputStream ;
@@ -211,6 +216,9 @@ public void EncodeScanBaseline(Component component, CancellationToken cancellati
211216 ref HuffmanLut dcHuffmanTable = ref this . dcHuffmanTables [ component . DcTableId ] ;
212217 ref HuffmanLut acHuffmanTable = ref this . acHuffmanTables [ component . AcTableId ] ;
213218
219+ int restarts = 0 ;
220+ int restartsToGo = this . restartInterval ;
221+
214222 for ( int i = 0 ; i < h ; i ++ )
215223 {
216224 cancellationToken . ThrowIfCancellationRequested ( ) ;
@@ -221,6 +229,13 @@ public void EncodeScanBaseline(Component component, CancellationToken cancellati
221229
222230 for ( nuint k = 0 ; k < ( uint ) w ; k ++ )
223231 {
232+ if ( this . restartInterval > 0 && restartsToGo == 0 )
233+ {
234+ this . FlushRemainingBytes ( ) ;
235+ this . WriteRestart ( restarts % 8 ) ;
236+ component . DcPredictor = 0 ;
237+ }
238+
224239 this . WriteBlock (
225240 component ,
226241 ref Unsafe . Add ( ref blockRef , k ) ,
@@ -231,6 +246,133 @@ ref Unsafe.Add(ref blockRef, k),
231246 {
232247 this . FlushToStream ( ) ;
233248 }
249+
250+ if ( this . restartInterval > 0 )
251+ {
252+ if ( restartsToGo == 0 )
253+ {
254+ restartsToGo = this . restartInterval ;
255+ restarts ++ ;
256+ }
257+
258+ restartsToGo -- ;
259+ }
260+ }
261+ }
262+
263+ this . FlushRemainingBytes ( ) ;
264+ }
265+
266+ /// <summary>
267+ /// Encodes the DC coefficients for a given component's blocks in a scan.
268+ /// </summary>
269+ /// <param name="component">The component whose DC coefficients need to be encoded.</param>
270+ /// <param name="cancellationToken">The token to request cancellation.</param>
271+ public void EncodeDcScan ( Component component , CancellationToken cancellationToken )
272+ {
273+ int h = component . HeightInBlocks ;
274+ int w = component . WidthInBlocks ;
275+
276+ ref HuffmanLut dcHuffmanTable = ref this . dcHuffmanTables [ component . DcTableId ] ;
277+
278+ int restarts = 0 ;
279+ int restartsToGo = this . restartInterval ;
280+
281+ for ( int i = 0 ; i < h ; i ++ )
282+ {
283+ cancellationToken . ThrowIfCancellationRequested ( ) ;
284+
285+ Span < Block8x8 > blockSpan = component . SpectralBlocks . DangerousGetRowSpan ( y : i ) ;
286+ ref Block8x8 blockRef = ref MemoryMarshal . GetReference ( blockSpan ) ;
287+
288+ for ( nuint k = 0 ; k < ( uint ) w ; k ++ )
289+ {
290+ if ( this . restartInterval > 0 && restartsToGo == 0 )
291+ {
292+ this . FlushRemainingBytes ( ) ;
293+ this . WriteRestart ( restarts % 8 ) ;
294+ component . DcPredictor = 0 ;
295+ }
296+
297+ this . WriteDc (
298+ component ,
299+ ref Unsafe . Add ( ref blockRef , k ) ,
300+ ref dcHuffmanTable ) ;
301+
302+ if ( this . IsStreamFlushNeeded )
303+ {
304+ this . FlushToStream ( ) ;
305+ }
306+
307+ if ( this . restartInterval > 0 )
308+ {
309+ if ( restartsToGo == 0 )
310+ {
311+ restartsToGo = this . restartInterval ;
312+ restarts ++ ;
313+ }
314+
315+ restartsToGo -- ;
316+ }
317+ }
318+ }
319+
320+ this . FlushRemainingBytes ( ) ;
321+ }
322+
323+ /// <summary>
324+ /// Encodes the AC coefficients for a specified range of blocks in a component's scan.
325+ /// </summary>
326+ /// <param name="component">The component whose AC coefficients need to be encoded.</param>
327+ /// <param name="start">The starting index of the AC coefficient range to encode.</param>
328+ /// <param name="end">The ending index of the AC coefficient range to encode.</param>
329+ /// <param name="cancellationToken">The token to request cancellation.</param>
330+ public void EncodeAcScan ( Component component , nint start , nint end , CancellationToken cancellationToken )
331+ {
332+ int h = component . HeightInBlocks ;
333+ int w = component . WidthInBlocks ;
334+
335+ int restarts = 0 ;
336+ int restartsToGo = this . restartInterval ;
337+
338+ ref HuffmanLut acHuffmanTable = ref this . acHuffmanTables [ component . AcTableId ] ;
339+
340+ for ( int i = 0 ; i < h ; i ++ )
341+ {
342+ cancellationToken . ThrowIfCancellationRequested ( ) ;
343+
344+ Span < Block8x8 > blockSpan = component . SpectralBlocks . DangerousGetRowSpan ( y : i ) ;
345+ ref Block8x8 blockRef = ref MemoryMarshal . GetReference ( blockSpan ) ;
346+
347+ for ( nuint k = 0 ; k < ( uint ) w ; k ++ )
348+ {
349+ if ( this . restartInterval > 0 && restartsToGo == 0 )
350+ {
351+ this . FlushRemainingBytes ( ) ;
352+ this . WriteRestart ( restarts % 8 ) ;
353+ }
354+
355+ this . WriteAcBlock (
356+ ref Unsafe . Add ( ref blockRef , k ) ,
357+ start ,
358+ end ,
359+ ref acHuffmanTable ) ;
360+
361+ if ( this . IsStreamFlushNeeded )
362+ {
363+ this . FlushToStream ( ) ;
364+ }
365+
366+ if ( this . restartInterval > 0 )
367+ {
368+ if ( restartsToGo == 0 )
369+ {
370+ restartsToGo = this . restartInterval ;
371+ restarts ++ ;
372+ }
373+
374+ restartsToGo -- ;
375+ }
234376 }
235377 }
236378
@@ -250,6 +392,9 @@ private void EncodeScanBaselineInterleaved<TPixel>(JpegFrame frame, SpectralConv
250392 int mcusPerColumn = frame . McusPerColumn ;
251393 int mcusPerLine = frame . McusPerLine ;
252394
395+ int restarts = 0 ;
396+ int restartsToGo = this . restartInterval ;
397+
253398 for ( int j = 0 ; j < mcusPerColumn ; j ++ )
254399 {
255400 cancellationToken . ThrowIfCancellationRequested ( ) ;
@@ -260,6 +405,16 @@ private void EncodeScanBaselineInterleaved<TPixel>(JpegFrame frame, SpectralConv
260405 // Encode spectral to binary
261406 for ( int i = 0 ; i < mcusPerLine ; i ++ )
262407 {
408+ if ( this . restartInterval > 0 && restartsToGo == 0 )
409+ {
410+ this . FlushRemainingBytes ( ) ;
411+ this . WriteRestart ( restarts % 8 ) ;
412+ foreach ( var component in frame . Components )
413+ {
414+ component . DcPredictor = 0 ;
415+ }
416+ }
417+
263418 // Scan an interleaved mcu... process components in order
264419 int mcuCol = mcu % mcusPerLine ;
265420 for ( int k = 0 ; k < frame . Components . Length ; k ++ )
@@ -300,6 +455,17 @@ ref Unsafe.Add(ref blockRef, blockCol),
300455 {
301456 this . FlushToStream ( ) ;
302457 }
458+
459+ if ( this . restartInterval > 0 )
460+ {
461+ if ( restartsToGo == 0 )
462+ {
463+ restartsToGo = this . restartInterval ;
464+ restarts ++ ;
465+ }
466+
467+ restartsToGo -- ;
468+ }
303469 }
304470 }
305471
@@ -371,25 +537,29 @@ ref Unsafe.Add(ref c2BlockRef, i),
371537 this . FlushRemainingBytes ( ) ;
372538 }
373539
374- private void WriteBlock (
540+ private void WriteDc (
375541 Component component ,
376542 ref Block8x8 block ,
377- ref HuffmanLut dcTable ,
378- ref HuffmanLut acTable )
543+ ref HuffmanLut dcTable )
379544 {
380545 // Emit the DC delta.
381546 int dc = block [ 0 ] ;
382547 this . EmitHuffRLE ( dcTable . Values , 0 , dc - component . DcPredictor ) ;
383548 component . DcPredictor = dc ;
549+ }
384550
551+ private void WriteAcBlock (
552+ ref Block8x8 block ,
553+ nint start ,
554+ nint end ,
555+ ref HuffmanLut acTable )
556+ {
385557 // Emit the AC components.
386558 int [ ] acHuffTable = acTable . Values ;
387559
388- nint lastValuableIndex = block . GetLastNonZeroIndex ( ) ;
389-
390560 int runLength = 0 ;
391561 ref short blockRef = ref Unsafe . As < Block8x8 , short > ( ref block ) ;
392- for ( nint zig = 1 ; zig <= lastValuableIndex ; zig ++ )
562+ for ( nint zig = start ; zig < end ; zig ++ )
393563 {
394564 const int zeroRun1 = 1 << 4 ;
395565 const int zeroRun16 = 16 << 4 ;
@@ -413,14 +583,25 @@ private void WriteBlock(
413583 }
414584
415585 // if mcu block contains trailing zeros - we must write end of block (EOB) value indicating that current block is over
416- // this can be done for any number of trailing zeros, even when all 63 ac values are zero
417- // (Block8x8F.Size - 1) == 63 - last index of the mcu elements
418- if ( lastValuableIndex != Block8x8F . Size - 1 )
586+ if ( runLength > 0 )
419587 {
420588 this . EmitHuff ( acHuffTable , 0x00 ) ;
421589 }
422590 }
423591
592+ private void WriteBlock (
593+ Component component ,
594+ ref Block8x8 block ,
595+ ref HuffmanLut dcTable ,
596+ ref HuffmanLut acTable )
597+ {
598+ this . WriteDc ( component , ref block , ref dcTable ) ;
599+ this . WriteAcBlock ( ref block , 1 , 64 , ref acTable ) ;
600+ }
601+
602+ private void WriteRestart ( int restart ) =>
603+ this . target . Write ( [ 0xff , ( byte ) ( JpegConstants . Markers . RST0 + restart ) ] , 0 , 2 ) ;
604+
424605 /// <summary>
425606 /// Emits the most significant count of bits to the buffer.
426607 /// </summary>
0 commit comments