diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
index d54246482..1c5cd4318 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
@@ -424,7 +424,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// Qt pointer
/// Unzig pointer
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe void QuantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr)
+ public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr)
{
float* b = (float*)blockPtr;
float* qtp = (float*)qtPtr;
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs
index 89c400bb0..4109fc10e 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs
@@ -38,8 +38,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
int QuantizationTableIndex { get; }
///
- /// Gets the storing the "raw" frequency-domain decoded blocks.
- /// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks.
+ /// Gets the storing the "raw" frequency-domain decoded + unzigged blocks.
+ /// We need to apply IDCT and dequantiazition to transform them into color-space blocks.
///
Buffer2D SpectralBlocks { get; }
}
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
index 689dd774e..ae1180a52 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
@@ -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
{
+ ///
+ /// Encapsulates the execution post-processing algorithms to be applied on a to produce a valid :
+ /// (1) Dequantization
+ /// (2) IDCT
+ /// (3) Color conversion form one of the -s into a buffer of RGBA values
+ /// (4) Packing pixels from the buffer.
+ /// These operations are executed in steps.
+ /// image rows are converted in one step,
+ /// which means that size of the allocated memory is limited (does not depend on ).
+ ///
internal class JpegImagePostProcessor : IDisposable
{
+ ///
+ /// The number of block rows to be processed in one Step.
+ ///
public const int BlockRowsPerStep = 4;
+ ///
+ /// The number of image pixel rows to be processed in one step.
+ ///
public const int PixelRowsPerStep = 4 * 8;
+ ///
+ /// Temporal buffer to store a row of colors.
+ ///
private readonly Buffer rgbaBuffer;
+ ///
+ /// The corresponding to the current determined by .
+ ///
private JpegColorConverter colorConverter;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The representing the uncompressed spectral Jpeg data
public JpegImagePostProcessor(IRawJpegData rawJpeg)
{
this.RawJpeg = rawJpeg;
@@ -33,16 +55,32 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
}
+ ///
+ /// Gets the instances.
+ ///
public JpegComponentPostProcessor[] ComponentProcessors { get; }
+ ///
+ /// Gets the to be processed.
+ ///
public IRawJpegData RawJpeg { get; }
+ ///
+ /// Gets the total number of post processor steps deduced from the height of the image and .
+ ///
public int NumberOfPostProcessorSteps { get; }
+ ///
+ /// Gets the size of the temporal buffers we need to allocate into .
+ ///
public Size PostProcessorBufferSize { get; }
- public int CurrentImageRowInPixels { get; private set; }
+ ///
+ /// Gets the value of the counter that grows by each step by .
+ ///
+ public int PixelRowCounter { get; private set; }
+ ///
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(Image destination)
+ ///
+ /// Process all pixels into 'destination'. The image dimensions should match .
+ ///
+ /// The pixel type
+ /// The destination image
+ public void PostProcess(Image destination)
where TPixel : struct, IPixel
{
- 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(Image destination)
+ ///
+ /// Execute one step rocessing pixel rows into 'destination'.
+ ///
+ /// The pixel type
+ /// The destination image.
+ public void DoPostProcessorStep(Image destination)
where TPixel : struct, IPixel
{
- 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(Image destination)
+ ///
+ /// Convert and copy row of colors into 'destination' starting at row .
+ ///
+ /// The pixel type
+ /// The destination image
+ private void ConvertColorsInto(Image destination)
where TPixel : struct, IPixel
{
- int maxY = Math.Min(destination.Height, this.CurrentImageRowInPixels + PixelRowsPerStep);
+ int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep);
Buffer2D[] 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);
diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockPostProcessor.cs
index 739953099..29149a186 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockPostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegBlockPostProcessor.cs
@@ -1,18 +1,15 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
+using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
using SixLabors.ImageSharp.Memory;
-using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F;
+using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{
- using System.Runtime.CompilerServices;
-
- using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
- using SixLabors.Primitives;
-
///
/// Encapsulates the implementation of processing "raw" -s into Jpeg image channels.
///
@@ -63,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
Block8x8F* b = this.pointers.SourceBlock;
- Block8x8F.QuantizeBlock(b, this.pointers.QuantiazationTable, this.pointers.Unzig);
+ Block8x8F.DequantizeBlock(b, this.pointers.QuantiazationTable, this.pointers.Unzig);
FastFloatingPointDCT.TransformIDCT(ref *b, ref this.data.WorkspaceBlock1, ref this.data.WorkspaceBlock2);
}
@@ -77,13 +74,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.QuantizeAndTransform(decoder, component, ref sourceBlock);
this.data.WorkspaceBlock1.NormalizeColorsInplace();
- Size divs = component.SubSamplingDivisors;
// To conform better to libjpeg we actually NEED TO loose precision here.
// This is because they store blocks as Int16 between all the operations.
// Unfortunately, we need to emulate this to be "more accurate" :(
this.data.WorkspaceBlock1.RoundInplace();
+ Size divs = component.SubSamplingDivisors;
this.data.WorkspaceBlock1.CopyTo(destArea, divs.Width, divs.Height);
}