@@ -108,7 +108,14 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
108
108
109
109
// Write scans with actual pixel data
110
110
using SpectralConverter < TPixel > spectralConverter = new ( frame , image , this . QuantizationTables ) ;
111
- this . WriteHuffmanScans ( frame , frameConfig , spectralConverter , scanEncoder , buffer , cancellationToken ) ;
111
+ if ( this . encoder . Progressive )
112
+ {
113
+ this . WriteProgressiveScans ( frame , frameConfig , spectralConverter , scanEncoder , buffer , cancellationToken ) ;
114
+ }
115
+ else
116
+ {
117
+ this . WriteHuffmanScans ( frame , frameConfig , spectralConverter , scanEncoder , buffer , cancellationToken ) ;
118
+ }
112
119
113
120
// Write the End Of Image marker.
114
121
this . WriteEndOfImageMarker ( buffer ) ;
@@ -569,7 +576,8 @@ private void WriteStartOfFrame(int width, int height, JpegFrameConfig frame, Spa
569
576
570
577
// Length (high byte, low byte), 8 + components * 3.
571
578
int markerlen = 8 + ( 3 * components . Length ) ;
572
- this . WriteMarkerHeader ( JpegConstants . Markers . SOF0 , markerlen , buffer ) ;
579
+ byte marker = this . encoder . Progressive ? JpegConstants . Markers . SOF2 : JpegConstants . Markers . SOF0 ;
580
+ this . WriteMarkerHeader ( marker , markerlen , buffer ) ;
573
581
buffer [ 5 ] = ( byte ) components . Length ;
574
582
buffer [ 0 ] = 8 ; // Data Precision. 8 for now, 12 and 16 bit jpegs not supported
575
583
buffer [ 1 ] = ( byte ) ( height >> 8 ) ;
@@ -603,7 +611,17 @@ private void WriteStartOfFrame(int width, int height, JpegFrameConfig frame, Spa
603
611
/// </summary>
604
612
/// <param name="components">The collecction of component configuration items.</param>
605
613
/// <param name="buffer">Temporary buffer.</param>
606
- private void WriteStartOfScan ( Span < JpegComponentConfig > components , Span < byte > buffer )
614
+ private void WriteStartOfScan ( Span < JpegComponentConfig > components , Span < byte > buffer ) =>
615
+ this . WriteStartOfScan ( components , buffer , 0x00 , 0x3f ) ;
616
+
617
+ /// <summary>
618
+ /// Writes the StartOfScan marker.
619
+ /// </summary>
620
+ /// <param name="components">The collecction of component configuration items.</param>
621
+ /// <param name="buffer">Temporary buffer.</param>
622
+ /// <param name="spectralStart">Start of spectral selection</param>
623
+ /// <param name="spectralEnd">End of spectral selection</param>
624
+ private void WriteStartOfScan ( Span < JpegComponentConfig > components , Span < byte > buffer , byte spectralStart , byte spectralEnd )
607
625
{
608
626
// Write the SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes:
609
627
// - the marker length "\x00\x0c",
@@ -636,8 +654,8 @@ private void WriteStartOfScan(Span<JpegComponentConfig> components, Span<byte> b
636
654
buffer [ i2 + 6 ] = ( byte ) tableSelectors ;
637
655
}
638
656
639
- buffer [ sosSize - 1 ] = 0x00 ; // Ss - Start of spectral selection.
640
- buffer [ sosSize ] = 0x3f ; // Se - End of spectral selection.
657
+ buffer [ sosSize - 1 ] = spectralStart ; // Ss - Start of spectral selection.
658
+ buffer [ sosSize ] = spectralEnd ; // Se - End of spectral selection.
641
659
buffer [ sosSize + 1 ] = 0x00 ; // Ah + Ah (Successive approximation bit position high + low)
642
660
this . outputStream . Write ( buffer , 0 , sosSize + 2 ) ;
643
661
}
@@ -700,6 +718,55 @@ private void WriteHuffmanScans<TPixel>(
700
718
}
701
719
}
702
720
721
+ /// <summary>
722
+ /// Writes the progressive scans
723
+ /// </summary>
724
+ /// <typeparam name="TPixel">The type of pixel format.</typeparam>
725
+ /// <param name="frame">The current frame.</param>
726
+ /// <param name="frameConfig">The frame configuration.</param>
727
+ /// <param name="spectralConverter">The spectral converter.</param>
728
+ /// <param name="encoder">The scan encoder.</param>
729
+ /// <param name="buffer">Temporary buffer.</param>
730
+ /// <param name="cancellationToken">The cancellation token.</param>
731
+ private void WriteProgressiveScans < TPixel > (
732
+ JpegFrame frame ,
733
+ JpegFrameConfig frameConfig ,
734
+ SpectralConverter < TPixel > spectralConverter ,
735
+ HuffmanScanEncoder encoder ,
736
+ Span < byte > buffer ,
737
+ CancellationToken cancellationToken )
738
+ where TPixel : unmanaged, IPixel < TPixel >
739
+ {
740
+ frame . AllocateComponents ( fullScan : true ) ;
741
+ spectralConverter . ConvertFull ( ) ;
742
+
743
+ Span < JpegComponentConfig > components = frameConfig . Components ;
744
+
745
+ // Phase 1: DC scan
746
+ for ( int i = 0 ; i < frame . Components . Length ; i ++ )
747
+ {
748
+ this . WriteStartOfScan ( components . Slice ( i , 1 ) , buffer , 0x00 , 0x00 ) ;
749
+
750
+ encoder . EncodeDcScan ( frame . Components [ i ] , cancellationToken ) ;
751
+ }
752
+
753
+ // Phase 2: AC scans
754
+ int acScans = this . encoder . ProgressiveScans - 1 ;
755
+ int valuesPerScan = 64 / acScans ;
756
+ for ( int scan = 0 ; scan < acScans ; scan ++ )
757
+ {
758
+ int start = Math . Max ( 1 , scan * valuesPerScan ) ;
759
+ int end = scan == acScans - 1 ? 64 : ( scan + 1 ) * valuesPerScan ;
760
+
761
+ for ( int i = 0 ; i < components . Length ; i ++ )
762
+ {
763
+ this . WriteStartOfScan ( components . Slice ( i , 1 ) , buffer , ( byte ) start , ( byte ) ( end - 1 ) ) ;
764
+
765
+ encoder . EncodeAcScan ( frame . Components [ i ] , start , end , cancellationToken ) ;
766
+ }
767
+ }
768
+ }
769
+
703
770
/// <summary>
704
771
/// Writes the header for a marker with the given length.
705
772
/// </summary>
0 commit comments