Skip to content

Commit 2c5c9f1

Browse files
Merge pull request #1845 from br3aker/jpeg-decoder-size-fix
Jpeg decoder mcu size fix
2 parents a8de2ec + 2a182d7 commit 2c5c9f1

File tree

6 files changed

+41
-20
lines changed

6 files changed

+41
-20
lines changed

src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ private void ParseBaselineData()
151151
if (this.componentsCount == this.frame.ComponentCount)
152152
{
153153
this.ParseBaselineDataInterleaved();
154+
this.spectralConverter.CommitConversion();
154155
}
155156
else
156157
{

src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
1313
/// </remarks>
1414
internal abstract class SpectralConverter
1515
{
16+
/// <summary>
17+
/// Gets a value indicating whether this converter has converted spectral
18+
/// data of the current image or not.
19+
/// </summary>
20+
protected bool Converted { get; private set; }
21+
1622
/// <summary>
1723
/// Injects jpeg image decoding metadata.
1824
/// </summary>
@@ -33,6 +39,19 @@ internal abstract class SpectralConverter
3339
/// </remarks>
3440
public abstract void ConvertStrideBaseline();
3541

42+
/// <summary>
43+
/// Marks current converter state as 'converted'.
44+
/// </summary>
45+
/// <remarks>
46+
/// This must be called only for baseline interleaved jpeg's.
47+
/// </remarks>
48+
public void CommitConversion()
49+
{
50+
DebugGuard.IsFalse(this.Converted, nameof(this.Converted), $"{nameof(this.CommitConversion)} must be called only once");
51+
52+
this.Converted = true;
53+
}
54+
3655
/// <summary>
3756
/// Gets the color converter.
3857
/// </summary>

src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Buffers;
6+
using System.Linq;
67
using System.Numerics;
78
using System.Threading;
89
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
@@ -29,8 +30,6 @@ internal class SpectralConverter<TPixel> : SpectralConverter, IDisposable
2930

3031
private Buffer2D<TPixel> pixelBuffer;
3132

32-
private int blockRowsPerStep;
33-
3433
private int pixelRowsPerStep;
3534

3635
private int pixelRowCounter;
@@ -41,8 +40,6 @@ public SpectralConverter(Configuration configuration, CancellationToken cancella
4140
this.cancellationToken = cancellationToken;
4241
}
4342

44-
private bool Converted => this.pixelRowCounter >= this.pixelBuffer.Height;
45-
4643
public Buffer2D<TPixel> GetPixelBuffer()
4744
{
4845
if (!this.Converted)
@@ -52,7 +49,7 @@ public Buffer2D<TPixel> GetPixelBuffer()
5249
for (int step = 0; step < steps; step++)
5350
{
5451
this.cancellationToken.ThrowIfCancellationRequested();
55-
this.ConvertNextStride(step);
52+
this.ConvertStride(step);
5653
}
5754
}
5855

@@ -65,26 +62,26 @@ public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
6562
MemoryAllocator allocator = this.configuration.MemoryAllocator;
6663

6764
// iteration data
68-
IJpegComponent c0 = frame.Components[0];
65+
int majorBlockWidth = frame.Components.Max((component) => component.SizeInBlocks.Width);
66+
int majorVerticalSamplingFactor = frame.Components.Max((component) => component.SamplingFactors.Height);
6967

7068
const int blockPixelHeight = 8;
71-
this.blockRowsPerStep = c0.SamplingFactors.Height;
72-
this.pixelRowsPerStep = this.blockRowsPerStep * blockPixelHeight;
69+
this.pixelRowsPerStep = majorVerticalSamplingFactor * blockPixelHeight;
7370

7471
// pixel buffer for resulting image
7572
this.pixelBuffer = allocator.Allocate2D<TPixel>(frame.PixelWidth, frame.PixelHeight);
7673
this.paddedProxyPixelRow = allocator.Allocate<TPixel>(frame.PixelWidth + 3);
7774

7875
// component processors from spectral to Rgba32
79-
var postProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, this.pixelRowsPerStep);
76+
const int blockPixelWidth = 8;
77+
var postProcessorBufferSize = new Size(majorBlockWidth * blockPixelWidth, this.pixelRowsPerStep);
8078
this.componentProcessors = new JpegComponentPostProcessor[frame.Components.Length];
8179
for (int i = 0; i < this.componentProcessors.Length; i++)
8280
{
8381
this.componentProcessors[i] = new JpegComponentPostProcessor(allocator, frame, jpegData, postProcessorBufferSize, frame.Components[i]);
8482
}
8583

8684
// single 'stride' rgba32 buffer for conversion between spectral and TPixel
87-
// this.rgbaBuffer = allocator.Allocate<Vector4>(frame.PixelWidth);
8885
this.rgbBuffer = allocator.Allocate<byte>(frame.PixelWidth * 3);
8986

9087
// color converter from Rgba32 to TPixel
@@ -95,18 +92,17 @@ public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
9592
public override void ConvertStrideBaseline()
9693
{
9794
// Convert next pixel stride using single spectral `stride'
98-
// Note that zero passing eliminates the need of virtual call from JpegComponentPostProcessor
99-
this.ConvertNextStride(spectralStep: 0);
95+
// Note that zero passing eliminates the need of virtual call
96+
// from JpegComponentPostProcessor
97+
this.ConvertStride(spectralStep: 0);
10098

101-
// Clear spectral stride - this is VERY important as jpeg possibly won't fill entire buffer each stride
102-
// Which leads to decoding artifacts
103-
// Note that this code clears all buffers of the post processors, it's their responsibility to allocate only single stride
10499
foreach (JpegComponentPostProcessor cpp in this.componentProcessors)
105100
{
106101
cpp.ClearSpectralBuffers();
107102
}
108103
}
109104

105+
/// <inheritdoc/>
110106
public void Dispose()
111107
{
112108
if (this.componentProcessors != null)
@@ -121,7 +117,7 @@ public void Dispose()
121117
this.paddedProxyPixelRow?.Dispose();
122118
}
123119

124-
private void ConvertNextStride(int spectralStep)
120+
private void ConvertStride(int spectralStep)
125121
{
126122
int maxY = Math.Min(this.pixelBuffer.Height, this.pixelRowCounter + this.pixelRowsPerStep);
127123

tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ public void Cleanup()
7474
7575
| Method | Mean | Error | StdDev |
7676
|------------------------------------ |----------:|----------:|----------:|
77-
| 'Baseline 4:4:4 Interleaved' | 12.710 ms | 0.1120 ms | 0.0990 ms |
78-
| 'Baseline 4:2:0 Interleaved' | 8.855 ms | 0.1447 ms | 0.1353 ms |
79-
| 'Baseline 4:0:0 (grayscale)' | 1.660 ms | 0.0106 ms | 0.0088 ms |
80-
| 'Progressive 4:2:0 Non-Interleaved' | 14.138 ms | 0.2797 ms | 0.3330 ms |
77+
| 'Baseline 4:4:4 Interleaved' | 11.781 ms | 0.0737 ms | 0.0654 ms |
78+
| 'Baseline 4:2:0 Interleaved' | 8.688 ms | 0.0345 ms | 0.0306 ms |
79+
| 'Baseline 4:0:0 (grayscale)' | 1.643 ms | 0.0092 ms | 0.0086 ms |
80+
| 'Progressive 4:2:0 Non-Interleaved' | 13.770 ms | 0.0928 ms | 0.0823 ms |
8181
*/

tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public partial class JpegDecoderTests
2020
TestImages.Jpeg.Baseline.Jpeg420Small,
2121
TestImages.Jpeg.Issues.Fuzz.AccessViolationException922,
2222
TestImages.Jpeg.Baseline.Jpeg444,
23+
TestImages.Jpeg.Baseline.Jpeg422,
2324
TestImages.Jpeg.Baseline.Bad.BadEOF,
2425
TestImages.Jpeg.Baseline.MultiScanBaselineCMYK,
2526
TestImages.Jpeg.Baseline.YcckSubsample1222,
@@ -100,6 +101,7 @@ public partial class JpegDecoderTests
100101
[TestImages.Jpeg.Baseline.Bad.BadEOF] = 0.38f / 100,
101102
[TestImages.Jpeg.Baseline.Bad.BadRST] = 0.0589f / 100,
102103

104+
[TestImages.Jpeg.Baseline.Jpeg422] = 0.0013f / 100,
103105
[TestImages.Jpeg.Baseline.Testorig420] = 0.38f / 100,
104106
[TestImages.Jpeg.Baseline.Jpeg420Small] = 0.287f / 100,
105107
[TestImages.Jpeg.Baseline.Turtle420] = 1.0f / 100,
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)