diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs index d5a9340d7..904ce00dd 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs @@ -18,6 +18,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowExceptionForErrorCode(this OrigDecoderErrorCode errorCode) { + // REMARK: If this method throws for an image that is expected to be decodable, + // consider using the ***Unsafe variant of the parsing method that asks for ThrowExceptionForErrorCode() + // then verify the error code + implement fallback logic manually! switch (errorCode) { case OrigDecoderErrorCode.NoError: diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index d88481ba4..88599808f 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -107,6 +107,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder return this.Bytes.ReadByte(this.InputStream); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public OrigDecoderErrorCode ReadByteUnsafe(out byte result) + { + this.LastErrorCode = this.Bytes.ReadByteUnsafe(this.InputStream, out result); + return this.LastErrorCode; + } + /// /// Decodes a single bit /// TODO: This method (and also the usages) could be optimized by batching! diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 33d625725..58513fd29 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -235,6 +235,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort // Check for the Start Of Image marker. this.InputProcessor.ReadFull(this.Temp, 0, 2); + if (this.Temp[0] != OrigJpegConstants.Markers.XFF || this.Temp[1] != OrigJpegConstants.Markers.SOI) { throw new ImageFormatException("Missing SOI marker."); @@ -247,6 +248,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort while (processBytes) { this.InputProcessor.ReadFull(this.Temp, 0, 2); + + if (this.InputProcessor.ReachedEOF) + { + // We've reached the end of the stream. + processBytes = false; + } + while (this.Temp[0] != 0xff) { // Strictly speaking, this is a format error. However, libjpeg is @@ -281,7 +289,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort { // Section B.1.1.2 says, "Any marker may optionally be preceded by any // number of fill bytes, which are bytes assigned code X'FF'". - marker = this.InputProcessor.ReadByte(); + this.InputProcessor.ReadByteUnsafe(out marker); + + if (this.InputProcessor.ReachedEOF) + { + // We've reached the end of the stream. + processBytes = false; + break; + } } // End Of Image. @@ -303,7 +318,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort // Read the 16-bit length of the segment. The value includes the 2 bytes for the // length itself, so we subtract 2 to get the number of remaining bytes. - this.InputProcessor.ReadFull(this.Temp, 0, 2); + this.InputProcessor.ReadFullUnsafe(this.Temp, 0, 2); int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2; if (remaining < 0) { @@ -351,12 +366,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort return; } - // when this is a progressive image this gets called a number of times - // need to know how many times this should be called in total. this.ProcessStartOfScanMarker(remaining); - if (this.InputProcessor.ReachedEOF || !this.IsProgressive) + if (this.InputProcessor.ReachedEOF) { - // if unexpeced EOF reached or this is not a progressive image we can stop processing bytes as we now have the image data. + // If unexpected EOF reached. We can stop processing bytes as we now have the image data. processBytes = false; } @@ -390,15 +403,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort { this.InputProcessor.Skip(remaining); } - else if (marker < OrigJpegConstants.Markers.SOF0) - { - // See Table B.1 "Marker code assignments". - throw new ImageFormatException("Unknown marker"); - } - else - { - throw new ImageFormatException("Unknown marker"); - } break; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 9d04cf354..139fa351b 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -41,8 +41,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Bad.BadEOF, - TestImages.Jpeg.Baseline.Bad.ExifUndefType, TestImages.Jpeg.Issues.MultiHuffmanBaseline394, + TestImages.Jpeg.Baseline.MultiScanBaselineCMYK }; public static string[] ProgressiveTestJpegs = @@ -51,14 +51,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF, TestImages.Jpeg.Issues.BadCoeffsProgressive178, TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159, - TestImages.Jpeg.Issues.BadZigZagProgressive385 + TestImages.Jpeg.Issues.BadZigZagProgressive385, + TestImages.Jpeg.Progressive.Bad.ExifUndefType }; private static readonly Dictionary CustomToleranceValues = new Dictionary { // Baseline: [TestImages.Jpeg.Baseline.Calliphora] = 0.00002f / 100, - [TestImages.Jpeg.Baseline.Bad.ExifUndefType] = 0.011f / 100, [TestImages.Jpeg.Baseline.Bad.BadEOF] = 0.38f / 100, [TestImages.Jpeg.Baseline.Testorig420] = 0.38f / 100, @@ -70,6 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [TestImages.Jpeg.Progressive.Fb] = 0.16f / 100, [TestImages.Jpeg.Progressive.Progress] = 0.31f / 100, [TestImages.Jpeg.Issues.BadZigZagProgressive385] = 0.23f / 100, + [TestImages.Jpeg.Progressive.Bad.ExifUndefType] = 0.011f / 100, }; public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.RgbaVector; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs index 423673ef9..ec4a42104 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs @@ -21,13 +21,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg420Small, TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Bad.BadEOF, - TestImages.Jpeg.Baseline.Bad.ExifUndefType, }; public static string[] ProgressiveTestJpegs = { TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, - TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF + TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF, + TestImages.Jpeg.Progressive.Bad.ExifUndefType, }; public JpegImagePostProcessorTests(ITestOutputHelper output) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 4508c6863..6816b8465 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -28,13 +28,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, TestImages.Jpeg.Baseline.Jpeg400, TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Testorig420, TestImages.Jpeg.Baseline.Jpeg420Small, TestImages.Jpeg.Baseline.Bad.BadEOF, - TestImages.Jpeg.Baseline.Bad.ExifUndefType, + TestImages.Jpeg.Baseline.MultiScanBaselineCMYK }; public static readonly string[] ProgressiveTestJpegs = { TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, - TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF + TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF, + TestImages.Jpeg.Progressive.Bad.ExifUndefType, }; public static readonly string[] AllTestJpegs = BaselineTestJpegs.Concat(ProgressiveTestJpegs).ToArray(); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index edeeebd28..5e7e9e3a7 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs @@ -272,7 +272,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void ExifTypeUndefined() { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Bad.ExifUndefType).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateImage(); Assert.NotNull(image); ExifProfile profile = image.MetaData.ExifProfile; diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 864c96332..f1f989581 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -85,6 +85,7 @@ namespace SixLabors.ImageSharp.Tests public static class Bad { public const string BadEOF = "Jpg/progressive/BadEofProgressive.jpg"; + public const string ExifUndefType = "Jpg/progressive/ExifUndefType.jpg"; } public static readonly string[] All = { Fb, Progress, Festzug }; @@ -95,7 +96,6 @@ namespace SixLabors.ImageSharp.Tests public static class Bad { public const string BadEOF = "Jpg/baseline/badeof.jpg"; - public const string ExifUndefType = "Jpg/baseline/ExifUndefType.jpg"; } public const string Cmyk = "Jpg/baseline/cmyk.jpg"; @@ -113,6 +113,7 @@ namespace SixLabors.ImageSharp.Tests public const string Jpeg444 = "Jpg/baseline/jpeg444.jpg"; public const string Jpeg420Small = "Jpg/baseline/jpeg420small.jpg"; public const string Testorig420 = "Jpg/baseline/testorig.jpg"; + public const string MultiScanBaselineCMYK = "Jpg/baseline/MultiScanBaselineCMYK.jpg"; public static readonly string[] All = { diff --git a/tests/Images/External b/tests/Images/External index 20f83891c..8714b94dc 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 20f83891ce75597486f5532010a8c5dea1419a4d +Subproject commit 8714b94dc4bab6788fcbb6254174db2b9c8f69c9 diff --git a/tests/Images/Input/Jpg/baseline/MultiScanBaselineCMYK.jpg b/tests/Images/Input/Jpg/baseline/MultiScanBaselineCMYK.jpg new file mode 100644 index 000000000..a158e0ba2 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/MultiScanBaselineCMYK.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96f29bee2175f34b9637d684f8336bc3e5d2bb2711cef352bc9def6ed4424d04 +size 47443 diff --git a/tests/Images/Input/Jpg/baseline/ExifUndefType.jpg b/tests/Images/Input/Jpg/progressive/ExifUndefType.jpg similarity index 100% rename from tests/Images/Input/Jpg/baseline/ExifUndefType.jpg rename to tests/Images/Input/Jpg/progressive/ExifUndefType.jpg