@@ -87,6 +87,8 @@ internal class HuffmanScanEncoder
87
87
/// </remarks>
88
88
private readonly byte [ ] streamWriteBuffer ;
89
89
90
+ private readonly int restartInterval ;
91
+
90
92
/// <summary>
91
93
/// Number of jagged bits stored in <see cref="accumulatedBits"/>
92
94
/// </summary>
@@ -103,13 +105,16 @@ internal class HuffmanScanEncoder
103
105
/// Initializes a new instance of the <see cref="HuffmanScanEncoder"/> class.
104
106
/// </summary>
105
107
/// <param name="blocksPerCodingUnit">Amount of encoded 8x8 blocks per single jpeg macroblock.</param>
108
+ /// <param name="restartInterval">Numbers of MCUs between restart markers.</param>
106
109
/// <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 )
108
111
{
109
112
int emitBufferByteLength = MaxBytesPerBlock * blocksPerCodingUnit ;
110
113
this . emitBuffer = new uint [ emitBufferByteLength / sizeof ( uint ) ] ;
111
114
this . emitWriteIndex = this . emitBuffer . Length ;
112
115
116
+ this . restartInterval = restartInterval ;
117
+
113
118
this . streamWriteBuffer = new byte [ emitBufferByteLength * OutputBufferLengthMultiplier ] ;
114
119
115
120
this . target = outputStream ;
@@ -211,6 +216,9 @@ public void EncodeScanBaseline(Component component, CancellationToken cancellati
211
216
ref HuffmanLut dcHuffmanTable = ref this . dcHuffmanTables [ component . DcTableId ] ;
212
217
ref HuffmanLut acHuffmanTable = ref this . acHuffmanTables [ component . AcTableId ] ;
213
218
219
+ int restarts = 0 ;
220
+ int restartsToGo = this . restartInterval ;
221
+
214
222
for ( int i = 0 ; i < h ; i ++ )
215
223
{
216
224
cancellationToken . ThrowIfCancellationRequested ( ) ;
@@ -221,6 +229,13 @@ public void EncodeScanBaseline(Component component, CancellationToken cancellati
221
229
222
230
for ( nuint k = 0 ; k < ( uint ) w ; k ++ )
223
231
{
232
+ if ( this . restartInterval > 0 && restartsToGo == 0 )
233
+ {
234
+ this . FlushRemainingBytes ( ) ;
235
+ this . WriteRestart ( restarts % 8 ) ;
236
+ component . DcPredictor = 0 ;
237
+ }
238
+
224
239
this . WriteBlock (
225
240
component ,
226
241
ref Unsafe . Add ( ref blockRef , k ) ,
@@ -231,6 +246,133 @@ ref Unsafe.Add(ref blockRef, k),
231
246
{
232
247
this . FlushToStream ( ) ;
233
248
}
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
+ }
234
376
}
235
377
}
236
378
@@ -250,6 +392,9 @@ private void EncodeScanBaselineInterleaved<TPixel>(JpegFrame frame, SpectralConv
250
392
int mcusPerColumn = frame . McusPerColumn ;
251
393
int mcusPerLine = frame . McusPerLine ;
252
394
395
+ int restarts = 0 ;
396
+ int restartsToGo = this . restartInterval ;
397
+
253
398
for ( int j = 0 ; j < mcusPerColumn ; j ++ )
254
399
{
255
400
cancellationToken . ThrowIfCancellationRequested ( ) ;
@@ -260,6 +405,16 @@ private void EncodeScanBaselineInterleaved<TPixel>(JpegFrame frame, SpectralConv
260
405
// Encode spectral to binary
261
406
for ( int i = 0 ; i < mcusPerLine ; i ++ )
262
407
{
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
+
263
418
// Scan an interleaved mcu... process components in order
264
419
int mcuCol = mcu % mcusPerLine ;
265
420
for ( int k = 0 ; k < frame . Components . Length ; k ++ )
@@ -300,6 +455,17 @@ ref Unsafe.Add(ref blockRef, blockCol),
300
455
{
301
456
this . FlushToStream ( ) ;
302
457
}
458
+
459
+ if ( this . restartInterval > 0 )
460
+ {
461
+ if ( restartsToGo == 0 )
462
+ {
463
+ restartsToGo = this . restartInterval ;
464
+ restarts ++ ;
465
+ }
466
+
467
+ restartsToGo -- ;
468
+ }
303
469
}
304
470
}
305
471
@@ -371,25 +537,29 @@ ref Unsafe.Add(ref c2BlockRef, i),
371
537
this . FlushRemainingBytes ( ) ;
372
538
}
373
539
374
- private void WriteBlock (
540
+ private void WriteDc (
375
541
Component component ,
376
542
ref Block8x8 block ,
377
- ref HuffmanLut dcTable ,
378
- ref HuffmanLut acTable )
543
+ ref HuffmanLut dcTable )
379
544
{
380
545
// Emit the DC delta.
381
546
int dc = block [ 0 ] ;
382
547
this . EmitHuffRLE ( dcTable . Values , 0 , dc - component . DcPredictor ) ;
383
548
component . DcPredictor = dc ;
549
+ }
384
550
551
+ private void WriteAcBlock (
552
+ ref Block8x8 block ,
553
+ nint start ,
554
+ nint end ,
555
+ ref HuffmanLut acTable )
556
+ {
385
557
// Emit the AC components.
386
558
int [ ] acHuffTable = acTable . Values ;
387
559
388
- nint lastValuableIndex = block . GetLastNonZeroIndex ( ) ;
389
-
390
560
int runLength = 0 ;
391
561
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 ++ )
393
563
{
394
564
const int zeroRun1 = 1 << 4 ;
395
565
const int zeroRun16 = 16 << 4 ;
@@ -413,14 +583,25 @@ private void WriteBlock(
413
583
}
414
584
415
585
// 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 )
419
587
{
420
588
this . EmitHuff ( acHuffTable , 0x00 ) ;
421
589
}
422
590
}
423
591
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
+
424
605
/// <summary>
425
606
/// Emits the most significant count of bits to the buffer.
426
607
/// </summary>
0 commit comments