From daccbfbccce21314d6d5d54e82888afcea9e59e7 Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Mon, 12 Jul 2021 18:26:21 +0300 Subject: [PATCH] Basement for spectral tests --- .../Decoder/SpectralConverter{TPixel}.cs | 12 +++---- .../Formats/Jpeg/JpegDecoderCore.cs | 25 +++++++++------ .../Formats/Jpg/SpectralJpegTests.cs | 31 +++++++++++++++++++ 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs index 6d38bde06e..6ad2bf00c1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs @@ -32,10 +32,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private int pixelRowCounter; - public SpectralConverter(Configuration configuration, CancellationToken ct) + public SpectralConverter(Configuration configuration, CancellationToken cancellationToken) { this.configuration = configuration; - this.cancellationToken = ct; + this.cancellationToken = cancellationToken; } private bool Converted => this.pixelRowCounter >= this.pixelBuffer.Height; @@ -90,10 +90,6 @@ 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); - // 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 @@ -101,6 +97,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { cpp.ClearSpectralBuffers(); } + + // Convert next pixel stride using single spectral `stride' + // Note that zero passing eliminates the need of virtual call from JpegComponentPostProcessor + this.ConvertNextStride(spectralStep: 0); } public override void Dispose() diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 37628e88d7..c7a5bc42c7 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -220,9 +220,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { using var spectralConverter = new SpectralConverter(this.Configuration, cancellationToken); - this.scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, cancellationToken); + var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, cancellationToken); - this.ParseStream(stream, cancellationToken: cancellationToken); + this.ParseStream(stream, scanDecoder, cancellationToken); this.InitExifProfile(); this.InitIccProfile(); this.InitIptcProfile(); @@ -234,7 +234,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { - this.ParseStream(stream, true, cancellationToken); + this.ParseStream(stream, scanDecoder: null, cancellationToken); this.InitExifProfile(); this.InitIccProfile(); this.InitIptcProfile(); @@ -244,13 +244,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } /// - /// Parses the input stream for file markers + /// Parses the input stream for file markers. /// - /// The input stream - /// Whether to decode metadata only. - /// The token to monitor cancellation. - private void ParseStream(BufferedReadStream stream, bool metadataOnly = false, CancellationToken cancellationToken = default) + /// The input stream. + /// Scan decoder used exclusively to decode SOS marker. + /// The token to monitor cancellation. + internal void ParseStream(BufferedReadStream stream, HuffmanScanDecoder scanDecoder, CancellationToken ct) { + bool metadataOnly = scanDecoder == null; + + this.scanDecoder = scanDecoder; + this.Metadata = new ImageMetadata(); // Check for the Start Of Image marker. @@ -279,7 +283,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg while (fileMarker.Marker != JpegConstants.Markers.EOI || (fileMarker.Marker == JpegConstants.Markers.EOI && fileMarker.Invalid)) { - cancellationToken.ThrowIfCancellationRequested(); + ct.ThrowIfCancellationRequested(); if (!fileMarker.Invalid) { @@ -297,7 +301,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg case JpegConstants.Markers.SOS: if (!metadataOnly) { - this.ProcessStartOfScanMarker(stream, cancellationToken); + this.ProcessStartOfScanMarker(stream, ct); break; } else @@ -1030,6 +1034,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } int selectorsCount = stream.ReadByte(); + this.Frame.MultiScan = this.Frame.ComponentCount != selectorsCount; for (int i = 0; i < selectorsCount; i++) { int componentIndex = -1; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 8e787e7255..3c7855367f 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; @@ -76,6 +77,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using var ms = new MemoryStream(sourceBytes); using var bufferedStream = new BufferedReadStream(Configuration.Default, ms); + using var spectralConverter = new SpectralConverter(Configuration.Default, cancellationToken: default); + + var scanDecoder = new HuffmanScanDecoder(bufferedStream, spectralConverter, cancellationToken: default); + using Image image = decoder.Decode(bufferedStream, cancellationToken: default); var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); @@ -126,5 +131,31 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.True(totalDifference < tolerance); } + + private class DebugSpectralConverter : SpectralConverter + where TPixel : unmanaged, IPixel + { + private readonly SpectralConverter converter; + + public DebugSpectralConverter(SpectralConverter converter) + { + this.converter = converter; + } + + public override void ConvertStrideBaseline() + { + this.converter.ConvertStrideBaseline(); + } + + public override void Dispose() + { + this.converter?.Dispose(); + } + + public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData) + { + this.converter.InjectFrameData(frame, jpegData); + } + } } }