@@ -108,7 +108,14 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
108108
109109 // Write scans with actual pixel data
110110 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+ }
112119
113120 // Write the End Of Image marker.
114121 this . WriteEndOfImageMarker ( buffer ) ;
@@ -569,7 +576,8 @@ private void WriteStartOfFrame(int width, int height, JpegFrameConfig frame, Spa
569576
570577 // Length (high byte, low byte), 8 + components * 3.
571578 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 ) ;
573581 buffer [ 5 ] = ( byte ) components . Length ;
574582 buffer [ 0 ] = 8 ; // Data Precision. 8 for now, 12 and 16 bit jpegs not supported
575583 buffer [ 1 ] = ( byte ) ( height >> 8 ) ;
@@ -603,7 +611,17 @@ private void WriteStartOfFrame(int width, int height, JpegFrameConfig frame, Spa
603611 /// </summary>
604612 /// <param name="components">The collecction of component configuration items.</param>
605613 /// <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 )
607625 {
608626 // Write the SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes:
609627 // - the marker length "\x00\x0c",
@@ -636,8 +654,8 @@ private void WriteStartOfScan(Span<JpegComponentConfig> components, Span<byte> b
636654 buffer [ i2 + 6 ] = ( byte ) tableSelectors ;
637655 }
638656
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.
641659 buffer [ sosSize + 1 ] = 0x00 ; // Ah + Ah (Successive approximation bit position high + low)
642660 this . outputStream . Write ( buffer , 0 , sosSize + 2 ) ;
643661 }
@@ -700,6 +718,55 @@ private void WriteHuffmanScans<TPixel>(
700718 }
701719 }
702720
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+
703770 /// <summary>
704771 /// Writes the header for a marker with the given length.
705772 /// </summary>
0 commit comments