diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentExtensions.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentExtensions.cs
deleted file mode 100644
index d7fb52a790..0000000000
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentExtensions.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System.Runtime.CompilerServices;
-
-using SixLabors.ImageSharp.Memory;
-
-namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
-{
- ///
- /// Extension methods for
- ///
- internal static class JpegComponentExtensions
- {
- ///
- /// Gets a reference to the at the given row and column index from
- ///
- /// The
- /// The column
- /// The row
- /// The
- [MethodImpl(InliningOptions.ShortMethod)]
- public static ref Block8x8 GetBlockReference(this IJpegComponent component, int column, int row)
- {
- return ref component.SpectralBlocks.GetRowSpan(row)[column];
- }
-
- ///
- /// Gets a reference to the first item in a block
- /// at the given row and column index from
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static ref short GetBlockDataReference(this IJpegComponent component, int column, int row)
- {
- ref Block8x8 blockRef = ref component.GetBlockReference(column, row);
- return ref Unsafe.As(ref blockRef);
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
index 39b9792ac9..ec9805309e 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
@@ -1,8 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.IO;
+using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
@@ -179,10 +181,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
+ int mcuRow = mcu / mcusPerLine;
+
// Scan out an mcu's worth of this component; that's just determined
// by the basic H and V specified for the component
for (int y = 0; y < v; y++)
{
+ int blockRow = (mcuRow * v) + y;
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
for (int x = 0; x < h; x++)
{
if (this.eof)
@@ -190,15 +196,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
- int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockBaseline(
component,
- blockRow,
- blockCol,
+ ref blockSpan[blockCol],
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
@@ -236,6 +239,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0;
for (int j = 0; j < h; j++)
{
+ // TODO: Isn't blockRow == j actually?
+ int blockRow = mcu / w;
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
+
for (int i = 0; i < w; i++)
{
if (this.eof)
@@ -243,13 +250,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- int blockRow = mcu / w;
+ // TODO: Isn't blockCol == i actually?
int blockCol = mcu % w;
this.DecodeBlockBaseline(
component,
- blockRow,
- blockCol,
+ ref blockSpan[blockCol],
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
@@ -299,6 +305,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// by the basic H and V specified for the component
for (int y = 0; y < v; y++)
{
+ int mcuRow = mcu / mcusPerLine;
+ int blockRow = (mcuRow * v) + y;
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
+
for (int x = 0; x < h; x++)
{
if (this.eof)
@@ -306,15 +316,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
- int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockProgressiveDC(
component,
- blockRow,
- blockCol,
+ ref blockSpan[blockCol],
ref dcHuffmanTable);
}
}
@@ -351,6 +358,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0;
for (int j = 0; j < h; j++)
{
+ // TODO: isn't blockRow == j actually?
+ int blockRow = mcu / w;
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
+
for (int i = 0; i < w; i++)
{
if (this.eof)
@@ -358,23 +369,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- int blockRow = mcu / w;
+ // TODO: isn't blockCol == i actually?
int blockCol = mcu % w;
+ ref Block8x8 block = ref blockSpan[blockCol];
+
if (this.spectralStart == 0)
{
this.DecodeBlockProgressiveDC(
component,
- blockRow,
- blockCol,
+ ref block,
ref dcHuffmanTable);
}
else
{
this.DecodeBlockProgressiveAC(
component,
- blockRow,
- blockCol,
+ ref block,
ref acHuffmanTable,
ref fastACRef);
}
@@ -391,8 +402,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockBaseline(
JpegComponent component,
- int row,
- int col,
+ ref Block8x8 block,
ref HuffmanTable dcTable,
ref HuffmanTable acTable,
ref short fastACRef)
@@ -405,7 +415,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
JpegThrowHelper.ThrowBadHuffmanCode();
}
- ref short blockDataRef = ref component.GetBlockDataReference(col, row);
+ ref short blockDataRef = ref Unsafe.As(ref block);
int diff = t != 0 ? this.ExtendReceive(t) : 0;
int dc = component.DcPredictor + diff;
@@ -470,8 +480,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockProgressiveDC(
JpegComponent component,
- int row,
- int col,
+ ref Block8x8 block,
ref HuffmanTable dcTable)
{
if (this.spectralEnd != 0)
@@ -481,7 +490,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.CheckBits();
- ref short blockDataRef = ref component.GetBlockDataReference(col, row);
+ ref short blockDataRef = ref Unsafe.As(ref block);
if (this.successiveHigh == 0)
{
@@ -506,8 +515,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockProgressiveAC(
JpegComponent component,
- int row,
- int col,
+ ref Block8x8 block,
ref HuffmanTable acTable,
ref short fastACRef)
{
@@ -516,7 +524,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC.");
}
- ref short blockDataRef = ref component.GetBlockDataReference(col, row);
+ ref short blockDataRef = ref Unsafe.As(ref block);
if (this.successiveHigh == 0)
{
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
index e161bca05b..9292d5f7de 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
@@ -46,15 +46,19 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
+ // NOTE:
+ // The scaled result for very large image "TestImages.Jpeg.Issues.ExifGetString750Transform"
+ // is almost the same as the result for Jpeg420Exif,
+ // which proves that the execution time for the most common YCbCr 420 path scales linearly
[Params(
TestImages.Jpeg.Baseline.Lake,
TestImages.Jpeg.Issues.BadRstProgressive518,
- TestImages.Jpeg.Issues.ExifGetString750Transform,
TestImages.Jpeg.Baseline.Jpeg420Exif
)]
public string TestImage { get; set; }
+
[GlobalSetup]
public void ReadImages()
{
@@ -110,16 +114,16 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
// RESULTS (2018 November 4):
// Method | TestImage | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
// ------------------------------- |-------------------------------------------- |-----------:|-----------:|----------:|-------:|---------:|----------:|---------:|---------:|------------:|
- // 'Decode Jpeg - System.Drawing' | Jpg/baseline/Lake.jpg | 6.291 ms | 1.200 ms | 0.0678 ms | 1.00 | 0.00 | 62.5000 | - | - | 205.83 KB |
- // 'Decode Jpeg - ImageSharp' | Jpg/baseline/Lake.jpg | 18.493 ms | 3.025 ms | 0.1709 ms | 2.94 | 0.03 | - | - | - | 19.97 KB |
+ // 'Decode Jpeg - System.Drawing' | Jpg/baseline/Lake.jpg | 6.117 ms | 0.3923 ms | 0.0222 ms | 1.00 | 0.00 | 62.5000 | - | - | 205.83 KB |
+ // 'Decode Jpeg - ImageSharp' | Jpg/baseline/Lake.jpg | 18.126 ms | 0.6023 ms | 0.0340 ms | 2.96 | 0.01 | - | - | - | 19.97 KB |
// | | | | | | | | | | |
- // 'Decode Jpeg - System.Drawing' | Jpg/baseline/jpeg420exif.jpg | 16.962 ms | 1.446 ms | 0.0817 ms | 1.00 | 0.00 | 218.7500 | - | - | 757.04 KB |
- // 'Decode Jpeg - ImageSharp' | Jpg/baseline/jpeg420exif.jpg | 42.105 ms | 4.496 ms | 0.2540 ms | 2.48 | 0.02 | - | - | - | 21.94 KB |
+ // 'Decode Jpeg - System.Drawing' | Jpg/baseline/jpeg420exif.jpg | 17.063 ms | 2.6096 ms | 0.1474 ms | 1.00 | 0.00 | 218.7500 | - | - | 757.04 KB |
+ // 'Decode Jpeg - ImageSharp' | Jpg/baseline/jpeg420exif.jpg | 41.366 ms | 1.0115 ms | 0.0572 ms | 2.42 | 0.02 | - | - | - | 21.94 KB |
// | | | | | | | | | | |
- // 'Decode Jpeg - System.Drawing' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 432.344 ms | 89.746 ms | 5.0708 ms | 1.00 | 0.00 | 2375.0000 | - | - | 7403.76 KB |
- // 'Decode Jpeg - ImageSharp' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 421.292 ms | 128.587 ms | 7.2654 ms | 0.97 | 0.02 | 125.0000 | 125.0000 | 125.0000 | 35186.98 KB |
+ // 'Decode Jpeg - System.Drawing' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 428.282 ms | 94.9163 ms | 5.3629 ms | 1.00 | 0.00 | 2375.0000 | - | - | 7403.76 KB |
+ // 'Decode Jpeg - ImageSharp' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 386.698 ms | 33.0065 ms | 1.8649 ms | 0.90 | 0.01 | 125.0000 | 125.0000 | 125.0000 | 35186.97 KB |
// | | | | | | | | | | |
- // 'Decode Jpeg - System.Drawing' | Jpg/issues/issue750-exif-tranform.jpg | 94.723 ms | 4.663 ms | 0.2635 ms | 1.00 | 0.00 | 1750.0000 | - | - | 5492.63 KB |
- // 'Decode Jpeg - ImageSharp' | Jpg/issues/issue750-exif-tranform.jpg | 234.071 ms | 37.979 ms | 2.1459 ms | 2.47 | 0.02 | 312.5000 | 312.5000 | 312.5000 | 58834.45 KB |
+ // 'Decode Jpeg - System.Drawing' | Jpg/issues/issue750-exif-tranform.jpg | 95.192 ms | 3.1762 ms | 0.1795 ms | 1.00 | 0.00 | 1750.0000 | - | - | 5492.63 KB |
+ // 'Decode Jpeg - ImageSharp' | Jpg/issues/issue750-exif-tranform.jpg | 230.158 ms | 48.8128 ms | 2.7580 ms | 2.42 | 0.02 | 312.5000 | 312.5000 | 312.5000 | 58834.66 KB |
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
index 7acce84cea..e4fcd10c5f 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
@@ -67,9 +67,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
for (int y = 0; y < result.HeightInBlocks; y++)
{
+ Span blockRow = c.SpectralBlocks.GetRowSpan(y);
for (int x = 0; x < result.WidthInBlocks; x++)
{
- short[] data = c.GetBlockReference(x, y).ToArray();
+ short[] data = blockRow[x].ToArray();
result.MakeBlock(data, y, x);
}
}