diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
index bc9a53ea04..622657c48f 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
@@ -151,6 +151,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
if (this.componentsCount == this.frame.ComponentCount)
{
this.ParseBaselineDataInterleaved();
+ this.spectralConverter.CommitConvertion();
}
else
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs
index e975b11fbb..aca9dc36b3 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs
@@ -13,6 +13,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
internal abstract class SpectralConverter
{
+ ///
+ /// Gets a value indicating whether this converter has converted spectral
+ /// data of the current image or not.
+ ///
+ protected bool Converted { get; private set; }
+
///
/// Injects jpeg image decoding metadata.
///
@@ -33,6 +39,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
public abstract void ConvertStrideBaseline();
+ ///
+ /// Marks current converter state as 'converted'.
+ ///
+ ///
+ /// This must be called only for baseline interleaved jpeg's.
+ ///
+ public void CommitConvertion()
+ {
+ DebugGuard.IsFalse(this.Converted, nameof(this.Converted), $"{nameof(this.CommitConvertion)} must be called only once");
+
+ this.Converted = true;
+ }
+
///
/// Gets the color converter.
///
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
index ec7f3e5c30..2e965e0ac3 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
@@ -3,6 +3,7 @@
using System;
using System.Buffers;
+using System.Linq;
using System.Numerics;
using System.Threading;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
@@ -29,8 +30,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private Buffer2D pixelBuffer;
- private int blockRowsPerStep;
-
private int pixelRowsPerStep;
private int pixelRowCounter;
@@ -41,8 +40,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.cancellationToken = cancellationToken;
}
- private bool Converted => this.pixelRowCounter >= this.pixelBuffer.Height;
-
public Buffer2D GetPixelBuffer()
{
if (!this.Converted)
@@ -52,7 +49,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int step = 0; step < steps; step++)
{
this.cancellationToken.ThrowIfCancellationRequested();
- this.ConvertNextStride(step);
+ this.ConvertStride(step);
}
}
@@ -65,18 +62,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
MemoryAllocator allocator = this.configuration.MemoryAllocator;
// iteration data
- IJpegComponent c0 = frame.Components[0];
+ int majorBlockWidth = frame.Components.Max((component) => component.SizeInBlocks.Width);
+ int majorVerticalSamplingFactor = frame.Components.Max((component) => component.SamplingFactors.Height);
const int blockPixelHeight = 8;
- this.blockRowsPerStep = c0.SamplingFactors.Height;
- this.pixelRowsPerStep = this.blockRowsPerStep * blockPixelHeight;
+ this.pixelRowsPerStep = majorVerticalSamplingFactor * blockPixelHeight;
// pixel buffer for resulting image
this.pixelBuffer = allocator.Allocate2D(frame.PixelWidth, frame.PixelHeight);
this.paddedProxyPixelRow = allocator.Allocate(frame.PixelWidth + 3);
// component processors from spectral to Rgba32
- var postProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, this.pixelRowsPerStep);
+ const int blockPixelWidth = 8;
+ var postProcessorBufferSize = new Size(majorBlockWidth * blockPixelWidth, this.pixelRowsPerStep);
this.componentProcessors = new JpegComponentPostProcessor[frame.Components.Length];
for (int i = 0; i < this.componentProcessors.Length; i++)
{
@@ -84,7 +82,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
// single 'stride' rgba32 buffer for conversion between spectral and TPixel
- // this.rgbaBuffer = allocator.Allocate(frame.PixelWidth);
this.rgbBuffer = allocator.Allocate(frame.PixelWidth * 3);
// color converter from Rgba32 to TPixel
@@ -95,18 +92,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
public override void ConvertStrideBaseline()
{
// Convert next pixel stride using single spectral `stride'
- // Note that zero passing eliminates the need of virtual call from JpegComponentPostProcessor
- this.ConvertNextStride(spectralStep: 0);
+ // Note that zero passing eliminates the need of virtual call
+ // from JpegComponentPostProcessor
+ this.ConvertStride(spectralStep: 0);
- // Clear spectral stride - this is VERY important as jpeg possibly won't fill entire buffer each stride
- // Which leads to decoding artifacts
- // Note that this code clears all buffers of the post processors, it's their responsibility to allocate only single stride
foreach (JpegComponentPostProcessor cpp in this.componentProcessors)
{
cpp.ClearSpectralBuffers();
}
}
+ ///
public void Dispose()
{
if (this.componentProcessors != null)
@@ -121,7 +117,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.paddedProxyPixelRow?.Dispose();
}
- private void ConvertNextStride(int spectralStep)
+ private void ConvertStride(int spectralStep)
{
int maxY = Math.Min(this.pixelBuffer.Height, this.pixelRowCounter + this.pixelRowsPerStep);