@@ -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,17 @@ 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+ }
234260 }
235261 }
236262
@@ -241,17 +267,16 @@ ref Unsafe.Add(ref blockRef, k),
241267 /// Encodes the DC coefficients for a given component's blocks in a scan.
242268 /// </summary>
243269 /// <param name="component">The component whose DC coefficients need to be encoded.</param>
244- /// <param name="restartInterval">Numbers of MCUs between restart markers.</param>
245270 /// <param name="cancellationToken">The token to request cancellation.</param>
246- public void EncodeDcScan ( Component component , int restartInterval , CancellationToken cancellationToken )
271+ public void EncodeDcScan ( Component component , CancellationToken cancellationToken )
247272 {
248273 int h = component . HeightInBlocks ;
249274 int w = component . WidthInBlocks ;
250275
251276 ref HuffmanLut dcHuffmanTable = ref this . dcHuffmanTables [ component . DcTableId ] ;
252277
253278 int restarts = 0 ;
254- int restartsToGo = restartInterval ;
279+ int restartsToGo = this . restartInterval ;
255280
256281 for ( int i = 0 ; i < h ; i ++ )
257282 {
@@ -262,7 +287,7 @@ public void EncodeDcScan(Component component, int restartInterval, CancellationT
262287
263288 for ( nuint k = 0 ; k < ( uint ) w ; k ++ )
264289 {
265- if ( restartInterval > 0 && restartsToGo == 0 )
290+ if ( this . restartInterval > 0 && restartsToGo == 0 )
266291 {
267292 this . FlushRemainingBytes ( ) ;
268293 this . WriteRestart ( restarts % 8 ) ;
@@ -279,13 +304,12 @@ ref Unsafe.Add(ref blockRef, k),
279304 this . FlushToStream ( ) ;
280305 }
281306
282- if ( restartInterval > 0 )
307+ if ( this . restartInterval > 0 )
283308 {
284309 if ( restartsToGo == 0 )
285310 {
286- restartsToGo = restartInterval ;
311+ restartsToGo = this . restartInterval ;
287312 restarts ++ ;
288- restarts &= 7 ;
289313 }
290314
291315 restartsToGo -- ;
@@ -302,15 +326,14 @@ ref Unsafe.Add(ref blockRef, k),
302326 /// <param name="component">The component whose AC coefficients need to be encoded.</param>
303327 /// <param name="start">The starting index of the AC coefficient range to encode.</param>
304328 /// <param name="end">The ending index of the AC coefficient range to encode.</param>
305- /// <param name="restartInterval">Numbers of MCUs between restart markers.</param>
306329 /// <param name="cancellationToken">The token to request cancellation.</param>
307- public void EncodeAcScan ( Component component , nint start , nint end , int restartInterval , CancellationToken cancellationToken )
330+ public void EncodeAcScan ( Component component , nint start , nint end , CancellationToken cancellationToken )
308331 {
309332 int h = component . HeightInBlocks ;
310333 int w = component . WidthInBlocks ;
311334
312335 int restarts = 0 ;
313- int restartsToGo = restartInterval ;
336+ int restartsToGo = this . restartInterval ;
314337
315338 ref HuffmanLut acHuffmanTable = ref this . acHuffmanTables [ component . AcTableId ] ;
316339
@@ -323,7 +346,7 @@ public void EncodeAcScan(Component component, nint start, nint end, int restartI
323346
324347 for ( nuint k = 0 ; k < ( uint ) w ; k ++ )
325348 {
326- if ( restartInterval > 0 && restartsToGo == 0 )
349+ if ( this . restartInterval > 0 && restartsToGo == 0 )
327350 {
328351 this . FlushRemainingBytes ( ) ;
329352 this . WriteRestart ( restarts % 8 ) ;
@@ -340,13 +363,12 @@ ref Unsafe.Add(ref blockRef, k),
340363 this . FlushToStream ( ) ;
341364 }
342365
343- if ( restartInterval > 0 )
366+ if ( this . restartInterval > 0 )
344367 {
345368 if ( restartsToGo == 0 )
346369 {
347- restartsToGo = restartInterval ;
370+ restartsToGo = this . restartInterval ;
348371 restarts ++ ;
349- restarts &= 7 ;
350372 }
351373
352374 restartsToGo -- ;
@@ -370,6 +392,9 @@ private void EncodeScanBaselineInterleaved<TPixel>(JpegFrame frame, SpectralConv
370392 int mcusPerColumn = frame . McusPerColumn ;
371393 int mcusPerLine = frame . McusPerLine ;
372394
395+ int restarts = 0 ;
396+ int restartsToGo = this . restartInterval ;
397+
373398 for ( int j = 0 ; j < mcusPerColumn ; j ++ )
374399 {
375400 cancellationToken . ThrowIfCancellationRequested ( ) ;
@@ -380,6 +405,16 @@ private void EncodeScanBaselineInterleaved<TPixel>(JpegFrame frame, SpectralConv
380405 // Encode spectral to binary
381406 for ( int i = 0 ; i < mcusPerLine ; i ++ )
382407 {
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+
383418 // Scan an interleaved mcu... process components in order
384419 int mcuCol = mcu % mcusPerLine ;
385420 for ( int k = 0 ; k < frame . Components . Length ; k ++ )
@@ -420,6 +455,17 @@ ref Unsafe.Add(ref blockRef, blockCol),
420455 {
421456 this . FlushToStream ( ) ;
422457 }
458+
459+ if ( this . restartInterval > 0 )
460+ {
461+ if ( restartsToGo == 0 )
462+ {
463+ restartsToGo = this . restartInterval ;
464+ restarts ++ ;
465+ }
466+
467+ restartsToGo -- ;
468+ }
423469 }
424470 }
425471
@@ -554,7 +600,7 @@ private void WriteBlock(
554600 }
555601
556602 private void WriteRestart ( int restart ) =>
557- this . target . Write ( [ 0xff , ( byte ) ( JpegConstants . Markers . RST0 + restart ) ] ) ;
603+ this . target . Write ( [ 0xff , ( byte ) ( JpegConstants . Markers . RST0 + restart ) ] , 0 , 2 ) ;
558604
559605 /// <summary>
560606 /// Emits the most significant count of bits to the buffer.
0 commit comments