diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
index 6ce7e92ec..3dda253d2 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
@@ -94,15 +94,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
///
public PdfJsFrame Frame { get; private set; }
+ ///
+ public Size ImageSizeInPixels { get; private set; }
+
+ ///
+ /// Gets the number of MCU blocks in the image as .
+ ///
+ public Size ImageSizeInMCU { get; private set; }
+
///
/// Gets the image width
///
- public int ImageWidth { get; private set; }
+ public int ImageWidth => this.ImageSizeInPixels.Width;
///
/// Gets the image height
///
- public int ImageHeight { get; private set; }
+ public int ImageHeight => this.ImageSizeInPixels.Height;
///
/// Gets the color depth, in number of bits per pixel.
@@ -124,17 +132,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
///
public ImageMetaData MetaData { get; private set; }
- ///
- public Size ImageSizeInPixels => new Size(this.ImageWidth, this.ImageHeight);
-
///
public int ComponentCount { get; private set; }
///
public JpegColorSpace ColorSpace { get; private set; }
+ ///
+ /// Gets the components.
+ ///
+ public PdfJsFrameComponent[] Components => this.Frame.Components;
+
///
- public IEnumerable Components => this.Frame.Components;
+ IEnumerable IRawJpegData.Components => this.Components;
///
public Block8x8F[] QuantizationTables { get; private set; }
@@ -367,7 +377,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
{
return JpegColorSpace.YCbCr;
}
- else if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
+
+ if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
{
return JpegColorSpace.RGB;
}
@@ -388,9 +399,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
///
private void AssignResolution()
{
- this.ImageWidth = this.Frame.SamplesPerLine;
- this.ImageHeight = this.Frame.Scanlines;
-
if (this.jFif.XDensity > 0 && this.jFif.YDensity > 0)
{
this.MetaData.HorizontalResolution = this.jFif.XDensity;
@@ -631,51 +639,50 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
ComponentCount = this.temp[5]
};
+ this.ImageSizeInPixels = new Size(this.Frame.SamplesPerLine, this.Frame.Scanlines);
+
int maxH = 0;
int maxV = 0;
int index = 6;
this.ComponentCount = this.Frame.ComponentCount;
+
if (!metadataOnly)
{
// No need to pool this. They max out at 4
this.Frame.ComponentIds = new byte[this.Frame.ComponentCount];
this.Frame.Components = new PdfJsFrameComponent[this.Frame.ComponentCount];
- }
-
- for (int i = 0; i < this.Frame.ComponentCount; i++)
- {
- byte hv = this.temp[index + 1];
- int h = hv >> 4;
- int v = hv & 15;
+ this.ColorSpace = this.DeduceJpegColorSpace();
- if (maxH < h)
+ for (int i = 0; i < this.Frame.ComponentCount; i++)
{
- maxH = h;
- }
+ byte hv = this.temp[index + 1];
+ int h = hv >> 4;
+ int v = hv & 15;
- if (maxV < v)
- {
- maxV = v;
- }
+ if (maxH < h)
+ {
+ maxH = h;
+ }
+
+ if (maxV < v)
+ {
+ maxV = v;
+ }
- if (!metadataOnly)
- {
var component = new PdfJsFrameComponent(this.configuration.MemoryManager, this.Frame, this.temp[index], h, v, this.temp[index + 2], i);
this.Frame.Components[i] = component;
this.Frame.ComponentIds[i] = component.Id;
- }
-
- index += 3;
- }
- this.Frame.MaxHorizontalFactor = maxH;
- this.Frame.MaxVerticalFactor = maxV;
+ index += 3;
+ }
- if (!metadataOnly)
- {
+ this.Frame.MaxHorizontalFactor = maxH;
+ this.Frame.MaxVerticalFactor = maxV;
+ this.ColorSpace = this.DeduceJpegColorSpace();
this.Frame.InitComponents();
+ this.ImageSizeInMCU = new Size(this.Frame.McusPerLine, this.Frame.McusPerColumn);
}
}
@@ -822,7 +829,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
private Image PostProcessIntoImage()
where TPixel : struct, IPixel
{
- this.ColorSpace = this.DeduceJpegColorSpace();
using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryManager, this))
{
var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs
index 079f94cd2..606b72cbf 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs
@@ -3,6 +3,7 @@
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
+using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@@ -53,11 +54,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Theory]
[WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32)]
[WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)]
- public void DoProcessorStep(TestImageProvider provider)
+ public void DoProcessorStepGolang(TestImageProvider provider)
where TPixel : struct, IPixel
{
string imageFile = provider.SourceFileOrDescription;
- using (GolangJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile))
+ using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile))
using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryManager, decoder))
using (var imageFrame = new ImageFrame(Configuration.Default.MemoryManager, decoder.ImageWidth, decoder.ImageHeight))
{
@@ -71,15 +72,70 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
+ [Theory]
+ [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)]
+ public void DoProcessorStepPdfJs(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ string imageFile = provider.SourceFileOrDescription;
+ using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile))
+ using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryManager, decoder))
+ using (var imageFrame = new ImageFrame(Configuration.Default.MemoryManager, decoder.ImageWidth, decoder.ImageHeight))
+ {
+ pp.DoPostProcessorStep(imageFrame);
+
+ JpegComponentPostProcessor[] cp = pp.ComponentProcessors;
+
+ SaveBuffer(cp[0], provider);
+ SaveBuffer(cp[1], provider);
+ SaveBuffer(cp[2], provider);
+ }
+ }
+
+ [Theory]
+ [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)]
+ public void PostProcessGolang(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ string imageFile = provider.SourceFileOrDescription;
+ using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile))
+ using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryManager, decoder))
+ using (var image = new Image(decoder.ImageWidth, decoder.ImageHeight))
+ {
+ pp.PostProcess(image.Frames.RootFrame);
+
+ image.DebugSave(provider);
+
+ ImagingTestCaseUtility testUtil = provider.Utility;
+ testUtil.TestGroupName = nameof(JpegDecoderTests);
+ testUtil.TestName = JpegDecoderTests.DecodeBaselineJpegOutputName;
+
+ using (Image referenceImage =
+ provider.GetReferenceOutputImage(appendPixelTypeToFileName: false))
+ {
+ ImageSimilarityReport report = ImageComparer.Exact.CompareImagesOrFrames(referenceImage, image);
+
+ this.Output.WriteLine($"*** {imageFile} ***");
+ this.Output.WriteLine($"Difference: {report.DifferencePercentageString}");
+
+ // ReSharper disable once PossibleInvalidOperationException
+ Assert.True(report.TotalNormalizedDifference.Value < 0.005f);
+ }
+ }
+ }
+
[Theory]
[WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32)]
[WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgba32)]
[WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)]
- public void PostProcess(TestImageProvider provider)
+ public void PostProcessPdfJs(TestImageProvider provider)
where TPixel : struct, IPixel
{
string imageFile = provider.SourceFileOrDescription;
- using (GolangJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile))
+ using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile))
using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryManager, decoder))
using (var image = new Image(decoder.ImageWidth, decoder.ImageHeight))
{
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs
index e26557424..827a459cd 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs
@@ -6,6 +6,8 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder;
+using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort;
+using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.Primitives;
@@ -28,20 +30,35 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(TestImages.Jpeg.Baseline.Jpeg400, JpegColorSpace.Grayscale)]
[InlineData(TestImages.Jpeg.Baseline.Ycck, JpegColorSpace.Ycck)]
[InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegColorSpace.Cmyk)]
- public void ColorSpace_IsDeducedCorrectly(string imageFile, object expectedColorSpaceValue)
+ public void ColorSpace_IsDeducedCorrectlyGolang(string imageFile, object expectedColorSpaceValue)
{
var expecteColorSpace = (JpegColorSpace)expectedColorSpaceValue;
- using (GolangJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile, false))
+ using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile))
+ {
+ Assert.Equal(expecteColorSpace, decoder.ColorSpace);
+ }
+ }
+
+ [Theory]
+ [InlineData(TestImages.Jpeg.Baseline.Testorig420, JpegColorSpace.YCbCr)]
+ [InlineData(TestImages.Jpeg.Baseline.Jpeg400, JpegColorSpace.Grayscale)]
+ [InlineData(TestImages.Jpeg.Baseline.Ycck, JpegColorSpace.Ycck)]
+ [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegColorSpace.Cmyk)]
+ public void ColorSpace_IsDeducedCorrectlyPdfJs(string imageFile, object expectedColorSpaceValue)
+ {
+ var expecteColorSpace = (JpegColorSpace)expectedColorSpaceValue;
+
+ using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile))
{
Assert.Equal(expecteColorSpace, decoder.ColorSpace);
}
}
[Fact]
- public void ComponentScalingIsCorrect_1ChannelJpeg()
+ public void ComponentScalingIsCorrect_1ChannelJpegGolang()
{
- using (GolangJpegDecoderCore decoder = JpegFixture.ParseStream(TestImages.Jpeg.Baseline.Jpeg400, false))
+ using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(TestImages.Jpeg.Baseline.Jpeg400))
{
Assert.Equal(1, decoder.ComponentCount);
Assert.Equal(1, decoder.Components.Length);
@@ -56,6 +73,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
+ [Fact]
+ public void ComponentScalingIsCorrect_1ChannelJpegPdfJs()
+ {
+ using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(TestImages.Jpeg.Baseline.Jpeg400))
+ {
+ Assert.Equal(1, decoder.ComponentCount);
+ Assert.Equal(1, decoder.Components.Length);
+
+ Size expectedSizeInBlocks = decoder.ImageSizeInPixels.DivideRoundUp(8);
+
+ Assert.Equal(expectedSizeInBlocks, decoder.ImageSizeInMCU);
+
+ var uniform1 = new Size(1, 1);
+ PdfJsFrameComponent c0 = decoder.Components[0];
+ VerifyJpeg.VerifyComponent(c0, expectedSizeInBlocks, uniform1, uniform1);
+ }
+ }
+
[Theory]
[InlineData(TestImages.Jpeg.Baseline.Jpeg444)]
[InlineData(TestImages.Jpeg.Baseline.Jpeg420Exif)]
@@ -63,11 +98,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(TestImages.Jpeg.Baseline.Testorig420)]
[InlineData(TestImages.Jpeg.Baseline.Ycck)]
[InlineData(TestImages.Jpeg.Baseline.Cmyk)]
- public void PrintComponentData(string imageFile)
+ public void PrintComponentDataGolang(string imageFile)
{
var sb = new StringBuilder();
- using (GolangJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile, false))
+ using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile))
{
sb.AppendLine(imageFile);
sb.AppendLine($"Size:{decoder.ImageSizeInPixels} MCU:{decoder.ImageSizeInMCU}");
@@ -80,6 +115,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
this.Output.WriteLine(sb.ToString());
}
+ [Theory]
+ [InlineData(TestImages.Jpeg.Baseline.Jpeg444)]
+ [InlineData(TestImages.Jpeg.Baseline.Jpeg420Exif)]
+ [InlineData(TestImages.Jpeg.Baseline.Jpeg420Small)]
+ [InlineData(TestImages.Jpeg.Baseline.Testorig420)]
+ [InlineData(TestImages.Jpeg.Baseline.Ycck)]
+ [InlineData(TestImages.Jpeg.Baseline.Cmyk)]
+ public void PrintComponentDataPdfJs(string imageFile)
+ {
+ var sb = new StringBuilder();
+
+ using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile))
+ {
+ sb.AppendLine(imageFile);
+ sb.AppendLine($"Size:{decoder.ImageSizeInPixels} MCU:{decoder.ImageSizeInMCU}");
+ PdfJsFrameComponent c0 = decoder.Components[0];
+ PdfJsFrameComponent c1 = decoder.Components[1];
+
+ sb.AppendLine($"Luma: SAMP: {c0.SamplingFactors} BLOCKS: {c0.SizeInBlocks}");
+ sb.AppendLine($"Chroma: {c1.SamplingFactors} BLOCKS: {c1.SizeInBlocks}");
+ }
+ this.Output.WriteLine(sb.ToString());
+ }
+
public static readonly TheoryData ComponentVerificationData = new TheoryData()
{
{ TestImages.Jpeg.Baseline.Jpeg444, 3, new Size(1, 1), new Size(1, 1) },
@@ -93,16 +152,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Theory]
[MemberData(nameof(ComponentVerificationData))]
- public void ComponentScalingIsCorrect_MultiChannelJpeg(
+ public void ComponentScalingIsCorrect_MultiChannelJpegGolang(
string imageFile,
int componentCount,
object expectedLumaFactors,
object expectedChromaFactors)
{
- Size fLuma = (Size)expectedLumaFactors;
- Size fChroma = (Size)expectedChromaFactors;
+ var fLuma = (Size)expectedLumaFactors;
+ var fChroma = (Size)expectedChromaFactors;
- using (GolangJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile, false))
+ using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile))
{
Assert.Equal(componentCount, decoder.ComponentCount);
Assert.Equal(componentCount, decoder.Components.Length);
@@ -130,5 +189,46 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
}
+
+
+ [Theory]
+ [MemberData(nameof(ComponentVerificationData))]
+ public void ComponentScalingIsCorrect_MultiChannelJpegPdfJs(
+ string imageFile,
+ int componentCount,
+ object expectedLumaFactors,
+ object expectedChromaFactors)
+ {
+ var fLuma = (Size)expectedLumaFactors;
+ var fChroma = (Size)expectedChromaFactors;
+
+ using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile))
+ {
+ Assert.Equal(componentCount, decoder.ComponentCount);
+ Assert.Equal(componentCount, decoder.Components.Length);
+
+ PdfJsFrameComponent c0 = decoder.Components[0];
+ PdfJsFrameComponent c1 = decoder.Components[1];
+ PdfJsFrameComponent c2 = decoder.Components[2];
+
+ var uniform1 = new Size(1, 1);
+
+ Size expectedLumaSizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(fLuma);
+
+ Size divisor = fLuma.DivideBy(fChroma);
+
+ Size expectedChromaSizeInBlocks = expectedLumaSizeInBlocks.DivideRoundUp(divisor);
+
+ VerifyJpeg.VerifyComponent(c0, expectedLumaSizeInBlocks, fLuma, uniform1);
+ VerifyJpeg.VerifyComponent(c1, expectedChromaSizeInBlocks, fChroma, divisor);
+ VerifyJpeg.VerifyComponent(c2, expectedChromaSizeInBlocks, fChroma, divisor);
+
+ if (componentCount == 4)
+ {
+ PdfJsFrameComponent c3 = decoder.Components[2];
+ VerifyJpeg.VerifyComponent(c3, expectedLumaSizeInBlocks, fLuma, uniform1);
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs
index bb6ade0e7..7fe98e2af 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs
@@ -11,6 +11,7 @@ using System.Text;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
+using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort;
using Xunit;
using Xunit.Abstractions;
@@ -111,7 +112,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
internal void Print8x8Data(Span data)
{
- StringBuilder bld = new StringBuilder();
+ var bld = new StringBuilder();
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
@@ -145,7 +146,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
}
internal void CompareBlocks(Block8x8 a, Block8x8 b, int tolerance) =>
- this.CompareBlocks(a.AsFloatBlock(), b.AsFloatBlock(), (float)tolerance + 1e-5f);
+ this.CompareBlocks(a.AsFloatBlock(), b.AsFloatBlock(), tolerance + 1e-5f);
internal void CompareBlocks(Block8x8F a, Block8x8F b, float tolerance)
=> this.CompareBlocks(a.ToArray(), b.ToArray(), tolerance);
@@ -174,7 +175,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
Assert.False(failed);
}
- internal static GolangJpegDecoderCore ParseStream(string testFileName, bool metaDataOnly = false)
+ internal static GolangJpegDecoderCore ParseGolangStream(string testFileName, bool metaDataOnly = false)
{
byte[] bytes = TestFile.Create(testFileName).Bytes;
using (var ms = new MemoryStream(bytes))
@@ -184,5 +185,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
return decoder;
}
}
+
+ internal static PdfJsJpegDecoderCore ParsePdfJsStream(string testFileName, bool metaDataOnly = false)
+ {
+ byte[] bytes = TestFile.Create(testFileName).Bytes;
+ using (var ms = new MemoryStream(bytes))
+ {
+ var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder());
+ decoder.ParseStream(ms, metaDataOnly);
+ return decoder;
+ }
+ }
}
}
\ No newline at end of file