@ -1,26 +1,48 @@
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Numerics ;
using System.Runtime.CompilerServices ;
using SixLabors.ImageSharp.ColorSpaces ;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce ;
using SixLabors.ImageSharp.Memory ;
using SixLabors.ImageSharp.PixelFormats ;
using SixLabors.Primitives ;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{
/// <summary>
/// Encapsulates the execution post-processing algorithms to be applied on a <see cref="IRawJpegData"/> to produce a valid <see cref="Image{TPixel}"/>: <br/>
/// (1) Dequantization <br/>
/// (2) IDCT <br/>
/// (3) Color conversion form one of the <see cref="JpegColorSpace"/>-s into a <see cref="Vector4"/> buffer of RGBA values <br/>
/// (4) Packing <see cref="Image{TPixel}"/> pixels from the <see cref="Vector4"/> buffer. <br/>
/// These operations are executed in <see cref="NumberOfPostProcessorSteps"/> steps.
/// <see cref="PixelRowsPerStep"/> image rows are converted in one step,
/// which means that size of the allocated memory is limited (does not depend on <see cref="ImageBase{TPixel}.Height"/>).
/// </summary>
internal class JpegImagePostProcessor : IDisposable
{
/// <summary>
/// The number of block rows to be processed in one Step.
/// </summary>
public const int BlockRowsPerStep = 4 ;
/// <summary>
/// The number of image pixel rows to be processed in one step.
/// </summary>
public const int PixelRowsPerStep = 4 * 8 ;
/// <summary>
/// Temporal buffer to store a row of colors.
/// </summary>
private readonly Buffer < Vector4 > rgbaBuffer ;
/// <summary>
/// The <see cref="JpegColorConverter"/> corresponding to the current <see cref="JpegColorSpace"/> determined by <see cref="IRawJpegData.ColorSpace"/>.
/// </summary>
private JpegColorConverter colorConverter ;
/// <summary>
/// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class.
/// </summary>
/// <param name="rawJpeg">The <see cref="IRawJpegData"/> representing the uncompressed spectral Jpeg data</param>
public JpegImagePostProcessor ( IRawJpegData rawJpeg )
{
this . RawJpeg = rawJpeg ;
@ -33,16 +55,32 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
this . colorConverter = JpegColorConverter . GetConverter ( rawJpeg . ColorSpace ) ;
}
/// <summary>
/// Gets the <see cref="JpegComponentPostProcessor"/> instances.
/// </summary>
public JpegComponentPostProcessor [ ] ComponentProcessors { get ; }
/// <summary>
/// Gets the <see cref="IRawJpegData"/> to be processed.
/// </summary>
public IRawJpegData RawJpeg { get ; }
/// <summary>
/// Gets the total number of post processor steps deduced from the height of the image and <see cref="PixelRowsPerStep"/>.
/// </summary>
public int NumberOfPostProcessorSteps { get ; }
/// <summary>
/// Gets the size of the temporal buffers we need to allocate into <see cref="JpegComponentPostProcessor.ColorBuffer"/>.
/// </summary>
public Size PostProcessorBufferSize { get ; }
public int CurrentImageRowInPixels { get ; private set ; }
/// <summary>
/// Gets the value of the counter that grows by each step by <see cref="PixelRowsPerStep"/>.
/// </summary>
public int PixelRowCounter { get ; private set ; }
/// <inheritdoc />
public void Dispose ( )
{
foreach ( JpegComponentPostProcessor cpp in this . ComponentProcessors )
@ -53,43 +91,60 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
this . rgbaBuffer . Dispose ( ) ;
}
public void DoPostProcessorStep < TPixel > ( Image < TPixel > destination )
/// <summary>
/// Process all pixels into 'destination'. The image dimensions should match <see cref="RawJpeg"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="destination">The destination image</param>
public void PostProcess < TPixel > ( Image < TPixel > destination )
where TPixel : struct , IPixel < TPixel >
{
foreach ( JpegComponentPostProcessor cpp in this . ComponentProcessors )
this . PixelRowCounter = 0 ;
if ( this . RawJpeg . ImageSizeInPixels ! = destination . Size ( ) )
{
cpp . CopyBlocksToColorBuffer ( ) ;
throw new ArgumentException ( "Input image is not of the size of the processed one!" ) ;
}
this . ConvertColors ( destination ) ;
this . CurrentImageRowInPixels + = PixelRowsPerStep ;
while ( this . PixelRowCounter < this . RawJpeg . ImageSizeInPixels . Height )
{
this . DoPostProcessorStep ( destination ) ;
}
}
public void PostProcess < TPixel > ( Image < TPixel > destination )
/// <summary>
/// Execute one step rocessing <see cref="PixelRowsPerStep"/> pixel rows into 'destination'.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="destination">The destination image.</param>
public void DoPostProcessorStep < TPixel > ( Image < TPixel > destination )
where TPixel : struct , IPixel < TPixel >
{
if ( this . RawJpeg . ImageSizeInPixels ! = destination . Size ( ) )
foreach ( JpegComponentPostProcessor cpp in this . ComponentProcessors )
{
throw new ArgumentException ( "Input image is not of the size of the processed one!" ) ;
cpp . CopyBlocksToColorBuffer ( ) ;
}
while ( this . CurrentImageRowInPixels < this . RawJpeg . ImageSizeInPixels . Height )
{
this . DoPostProcessorStep ( destination ) ;
}
this . ConvertColorsInto ( destination ) ;
this . PixelRowCounter + = PixelRowsPerStep ;
}
private void ConvertColors < TPixel > ( Image < TPixel > destination )
/// <summary>
/// Convert and copy <see cref="PixelRowsPerStep"/> row of colors into 'destination' starting at row <see cref="PixelRowCounter"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="destination">The destination image</param>
private void ConvertColorsInto < TPixel > ( Image < TPixel > destination )
where TPixel : struct , IPixel < TPixel >
{
int maxY = Math . Min ( destination . Height , this . CurrentImageRowInPixels + PixelRowsPerStep ) ;
int maxY = Math . Min ( destination . Height , this . PixelRowCounter + PixelRowsPerStep ) ;
Buffer2D < float > [ ] buffers = this . ComponentProcessors . Select ( cp = > cp . ColorBuffer ) . ToArray ( ) ;
for ( int yy = this . CurrentImageRowInPixels ; yy < maxY ; yy + + )
for ( int yy = this . PixelRowCounter ; yy < maxY ; yy + + )
{
int y = yy - this . CurrentImageRowInPixels ;
int y = yy - this . PixelRowCounter ;
var values = new JpegColorConverter . ComponentValues ( buffers , y ) ;
this . colorConverter . ConvertToRGBA ( values , this . rgbaBuffer ) ;