diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs new file mode 100644 index 0000000000..57d2221ba3 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs @@ -0,0 +1,61 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder +{ + internal unsafe struct FastACTable + { + /// + /// Gets the lookahead array + /// + public fixed short Lookahead[512]; + + /// + /// Derives a lookup table for fast AC entropy scan decoding. + /// This can happen multiple times during progressive decoding but always outside mcu loops. + /// + /// The AC Huffman table. + public void Derive(ref HuffmanTable huffmanTable) + { + const int FastBits = ScanDecoder.FastBits; + ref short fastACRef = ref this.Lookahead[0]; + ref byte huffmanLookaheadRef = ref huffmanTable.Lookahead[0]; + ref byte huffmanValuesRef = ref huffmanTable.Values[0]; + ref short huffmanSizesRef = ref huffmanTable.Sizes[0]; + + int i; + for (i = 0; i < (1 << FastBits); i++) + { + byte fast = Unsafe.Add(ref huffmanLookaheadRef, i); + Unsafe.Add(ref fastACRef, i) = 0; + + if (fast < byte.MaxValue) + { + int rs = Unsafe.Add(ref huffmanValuesRef, fast); + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = Unsafe.Add(ref huffmanSizesRef, fast); + + if (magbits != 0 && len + magbits <= FastBits) + { + // Magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FastBits) - 1)) >> (FastBits - magbits); + int m = 1 << (magbits - 1); + if (k < m) + { + k += (int)((~0U << magbits) + 1); + } + + // If the result is small enough, we can fit it in fastAC table + if (k >= -128 && k <= 127) + { + Unsafe.Add(ref fastACRef, i) = (short)((k << 8) + (run << 4) + (len + magbits)); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs deleted file mode 100644 index 06b46746a6..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - /// - /// The collection of lookup tables used for fast AC entropy scan decoding. - /// - internal sealed class FastACTables : IDisposable - { - private Buffer2D tables; - - /// - /// Initializes a new instance of the class. - /// - /// The memory allocator used to allocate memory for image processing operations. - public FastACTables(MemoryAllocator memoryAllocator) => this.tables = memoryAllocator.Allocate2D(512, 4, AllocationOptions.Clean); - - /// - /// Gets the representing the table at the index in the collection. - /// - /// The table index. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public ReadOnlySpan GetTableSpan(int index) => this.tables.GetRowSpan(index); - - /// - /// Gets a reference to the first element of the AC table indexed by - /// - /// The frame component. - [MethodImpl(InliningOptions.ShortMethod)] - public ref short GetAcTableReference(JpegComponent component) => ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0]; - - /// - /// Builds a lookup table for fast AC entropy scan decoding. - /// - /// The table index. - /// The collection of AC Huffman tables. - public unsafe void BuildACTableLut(int index, HuffmanTables acHuffmanTables) - { - const int FastBits = ScanDecoder.FastBits; - Span fastAC = this.tables.GetRowSpan(index); - ref HuffmanTable huffmanTable = ref acHuffmanTables[index]; - - int i; - for (i = 0; i < (1 << FastBits); i++) - { - byte fast = huffmanTable.Lookahead[i]; - fastAC[i] = 0; - if (fast < byte.MaxValue) - { - int rs = huffmanTable.Values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = huffmanTable.Sizes[fast]; - - if (magbits != 0 && len + magbits <= FastBits) - { - // Magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FastBits) - 1)) >> (FastBits - magbits); - int m = 1 << (magbits - 1); - if (k < m) - { - k += (int)((~0U << magbits) + 1); - } - - // if the result is small enough, we can fit it in fastAC table - if (k >= -128 && k <= 127) - { - fastAC[i] = (short)((k << 8) + (run << 4) + (len + magbits)); - } - } - } - } - } - - /// - public void Dispose() - { - this.tables?.Dispose(); - this.tables = null; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 90b3624651..a2c9bade19 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -17,6 +17,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder [StructLayout(LayoutKind.Sequential)] internal unsafe struct HuffmanTable { + private bool isDerived; + + private readonly MemoryAllocator memoryAllocator; + +#pragma warning disable IDE0044 // Add readonly modifier + private fixed byte codeLengths[17]; +#pragma warning restore IDE0044 // Add readonly modifier + /// /// Gets the max code array /// @@ -50,16 +58,31 @@ internal unsafe struct HuffmanTable /// The huffman values public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan codeLengths, ReadOnlySpan values) { + this.isDerived = false; + this.memoryAllocator = memoryAllocator; + Unsafe.CopyBlockUnaligned(ref this.codeLengths[0], ref MemoryMarshal.GetReference(codeLengths), (uint)codeLengths.Length); + Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), (uint)values.Length); + } + + /// + /// Expands the HuffmanTable into its derived form. + /// + public void Derive() + { + if (this.isDerived) + { + return; + } + const int Length = 257; - using (IMemoryOwner huffcode = memoryAllocator.Allocate(Length)) + using (IMemoryOwner huffcode = this.memoryAllocator.Allocate(Length)) { ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan()); - ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths); + ref byte codeLengthsRef = ref this.codeLengths[0]; // Figure C.1: make table of Huffman code length for each symbol ref short sizesRef = ref this.Sizes[0]; short x = 0; - for (short i = 1; i < 17; i++) { byte length = Unsafe.Add(ref codeLengthsRef, i); @@ -119,7 +142,7 @@ public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan codeLeng } } - Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), 256); + this.isDerived = true; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs deleted file mode 100644 index dc066aa0ac..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - /// - /// Defines a 2 pairs of huffman tables. - /// - internal sealed class HuffmanTables - { - private readonly HuffmanTable[] tables = new HuffmanTable[4]; - - /// - /// Gets or sets the table at the given index. - /// - /// The index - /// The - public ref HuffmanTable this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref this.tables[index]; - } - } -} \ 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 070652a9f9..ef4b359ff2 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -25,9 +26,9 @@ internal class ScanDecoder private static readonly int[] Bias = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 }; private readonly JpegFrame frame; - private readonly HuffmanTables dcHuffmanTables; - private readonly HuffmanTables acHuffmanTables; - private readonly FastACTables fastACTables; + private readonly HuffmanTable[] dcHuffmanTables; + private readonly HuffmanTable[] acHuffmanTables; + private readonly FastACTable[] fastACTables; private readonly DoubleBufferedStreamReader stream; private readonly JpegComponent[] components; @@ -95,9 +96,9 @@ internal class ScanDecoder public ScanDecoder( DoubleBufferedStreamReader stream, JpegFrame frame, - HuffmanTables dcHuffmanTables, - HuffmanTables acHuffmanTables, - FastACTables fastACTables, + HuffmanTable[] dcHuffmanTables, + HuffmanTable[] acHuffmanTables, + FastACTable[] fastACTables, int componentsLength, int restartInterval, int spectralStart, @@ -159,17 +160,35 @@ private void ParseBaselineData() } } - private void ParseBaselineDataInterleaved() + private unsafe void ParseBaselineDataInterleaved() { // Interleaved int mcu = 0; int mcusPerColumn = this.frame.McusPerColumn; int mcusPerLine = this.frame.McusPerLine; + + // Pre-derive the huffman table to avoid in-loop checks. + for (int i = 0; i < this.componentsLength; i++) + { + int order = this.frame.ComponentOrder[i]; + JpegComponent component = this.components[order]; + + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + dcHuffmanTable.Derive(); + acHuffmanTable.Derive(); + + ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; + fastAcTable.Derive(ref acHuffmanTable); + } + for (int j = 0; j < mcusPerColumn; j++) { for (int i = 0; i < mcusPerLine; i++) { // Scan an interleaved mcu... process components in order + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; for (int k = 0; k < this.componentsLength; k++) { int order = this.frame.ComponentOrder[k]; @@ -177,18 +196,20 @@ private void ParseBaselineDataInterleaved() ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); + ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; + ref short fastACRef = ref fastAcTable.Lookahead[0]; + 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); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); + for (int x = 0; x < h; x++) { if (this.eof) @@ -196,12 +217,11 @@ private void ParseBaselineDataInterleaved() return; } - int mcuCol = mcu % mcusPerLine; int blockCol = (mcuCol * h) + x; this.DecodeBlockBaseline( component, - ref blockSpan[blockCol], + ref Unsafe.Add(ref blockRef, blockCol), ref dcHuffmanTable, ref acHuffmanTable, ref fastACRef); @@ -225,7 +245,7 @@ private void ParseBaselineDataInterleaved() /// number of blocks to do just depends on how many actual "pixels" each component has, /// independent of interleaved MCU blocking and such. /// - private void ParseBaselineDataNonInterleaved() + private unsafe void ParseBaselineDataNonInterleaved() { JpegComponent component = this.components[this.frame.ComponentOrder[0]]; @@ -234,14 +254,19 @@ private void ParseBaselineDataNonInterleaved() ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); + dcHuffmanTable.Derive(); + acHuffmanTable.Derive(); + + ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; + fastAcTable.Derive(ref acHuffmanTable); + ref short fastACRef = ref fastAcTable.Lookahead[0]; 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); + int blockRow = j; + Span blockSpan = component.SpectralBlocks.GetRowSpan(j); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); for (int i = 0; i < w; i++) { @@ -250,12 +275,9 @@ private void ParseBaselineDataNonInterleaved() return; } - // TODO: Isn't blockCol == i actually? - int blockCol = mcu % w; - this.DecodeBlockBaseline( component, - ref blockSpan[blockCol], + ref Unsafe.Add(ref blockRef, i), ref dcHuffmanTable, ref acHuffmanTable, ref fastACRef); @@ -340,16 +362,29 @@ private void ParseProgressiveDataInterleaved() int mcu = 0; int mcusPerColumn = this.frame.McusPerColumn; int mcusPerLine = this.frame.McusPerLine; + + // Pre-derive the huffman table to avoid in-loop checks. + for (int k = 0; k < this.componentsLength; k++) + { + int order = this.frame.ComponentOrder[k]; + JpegComponent component = this.components[order]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + dcHuffmanTable.Derive(); + } + for (int j = 0; j < mcusPerColumn; j++) { for (int i = 0; i < mcusPerLine; i++) { // Scan an interleaved mcu... process components in order + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; for (int k = 0; k < this.componentsLength; k++) { int order = this.frame.ComponentOrder[k]; JpegComponent component = this.components[order]; ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -357,9 +392,9 @@ private void ParseProgressiveDataInterleaved() // 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); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); for (int x = 0; x < h; x++) { @@ -368,12 +403,11 @@ private void ParseProgressiveDataInterleaved() return; } - int mcuCol = mcu % mcusPerLine; int blockCol = (mcuCol * h) + x; this.DecodeBlockProgressiveDC( component, - ref blockSpan[blockCol], + ref Unsafe.Add(ref blockRef, blockCol), ref dcHuffmanTable); } } @@ -396,56 +430,78 @@ private void ParseProgressiveDataInterleaved() /// number of blocks to do just depends on how many actual "pixels" this /// component has, independent of interleaved MCU blocking and such /// - private void ParseProgressiveDataNonInterleaved() + private unsafe void ParseProgressiveDataNonInterleaved() { JpegComponent component = this.components[this.frame.ComponentOrder[0]]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; - ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); - - int mcu = 0; - for (int j = 0; j < h; j++) + if (this.spectralStart == 0) { - // TODO: isn't blockRow == j actually? - int blockRow = mcu / w; - Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + dcHuffmanTable.Derive(); - for (int i = 0; i < w; i++) + int mcu = 0; + for (int j = 0; j < h; j++) { - if (this.eof) - { - return; - } - - // TODO: isn't blockCol == i actually? - int blockCol = mcu % w; + Span blockSpan = component.SpectralBlocks.GetRowSpan(j); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); - ref Block8x8 block = ref blockSpan[blockCol]; - - if (this.spectralStart == 0) + for (int i = 0; i < w; i++) { + if (this.eof) + { + return; + } + this.DecodeBlockProgressiveDC( component, - ref block, + ref Unsafe.Add(ref blockRef, i), ref dcHuffmanTable); + + // Every data block is an MCU, so countdown the restart interval + mcu++; + if (!this.ContinueOnMcuComplete()) + { + return; + } } - else + } + } + else + { + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + acHuffmanTable.Derive(); + + ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId]; + fastAcTable.Derive(ref acHuffmanTable); + ref short fastACRef = ref fastAcTable.Lookahead[0]; + + int mcu = 0; + for (int j = 0; j < h; j++) + { + Span blockSpan = component.SpectralBlocks.GetRowSpan(j); + ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); + + for (int i = 0; i < w; i++) { + if (this.eof) + { + return; + } + this.DecodeBlockProgressiveAC( - ref block, + ref Unsafe.Add(ref blockRef, i), ref acHuffmanTable, ref fastACRef); - } - // Every data block is an MCU, so countdown the restart interval - mcu++; - if (!this.ContinueOnMcuComplete()) - { - return; + // Every data block is an MCU, so countdown the restart interval + mcu++; + if (!this.ContinueOnMcuComplete()) + { + return; + } } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 9bcfd9ff64..891dd52826 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -52,17 +52,17 @@ internal sealed class JpegDecoderCore : IRawJpegData /// /// The DC Huffman tables /// - private HuffmanTables dcHuffmanTables; + private HuffmanTable[] dcHuffmanTables; /// /// The AC Huffman tables /// - private HuffmanTables acHuffmanTables; + private HuffmanTable[] acHuffmanTables; /// /// The fast AC tables used for entropy decoding /// - private FastACTables fastACTables; + private FastACTable[] fastACTables; /// /// The reset interval determined by RST markers @@ -266,9 +266,10 @@ public void ParseStream(Stream stream, bool metadataOnly = false) // Only assign what we need if (!metadataOnly) { - this.dcHuffmanTables = new HuffmanTables(); - this.acHuffmanTables = new HuffmanTables(); - this.fastACTables = new FastACTables(this.configuration.MemoryAllocator); + const int maxTables = 4; + this.dcHuffmanTables = new HuffmanTable[maxTables]; + this.acHuffmanTables = new HuffmanTable[maxTables]; + this.fastACTables = new FastACTable[maxTables]; } // Break only when we discover a valid EOI marker. @@ -378,7 +379,6 @@ public void Dispose() { this.InputStream?.Dispose(); this.Frame?.Dispose(); - this.fastACTables?.Dispose(); // Set large fields to null. this.InputStream = null; @@ -872,12 +872,6 @@ private void ProcessDefineHuffmanTablesMarker(int remaining) tableIndex, codeLengths.GetSpan(), huffmanValues.GetSpan()); - - if (tableType != 0) - { - // Build a table that decodes both magnitude and value of small ACs in one go. - this.fastACTables.BuildACTableLut(tableIndex, this.acHuffmanTables); - } } } } @@ -967,7 +961,7 @@ private void ProcessStartOfScanMarker() /// The codelengths /// The values [MethodImpl(InliningOptions.ShortMethod)] - private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values) + private void BuildHuffmanTable(HuffmanTable[] tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values) => tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values); /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index ff49de2481..8a94826077 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -79,7 +79,8 @@ public partial class JpegDecoderTests TestImages.Jpeg.Issues.Fuzz.ArgumentException826A, TestImages.Jpeg.Issues.Fuzz.ArgumentException826B, TestImages.Jpeg.Issues.Fuzz.ArgumentException826C, - TestImages.Jpeg.Issues.Fuzz.AccessViolationException827 + TestImages.Jpeg.Issues.Fuzz.AccessViolationException827, + TestImages.Jpeg.Issues.Fuzz.ExecutionEngineException839 }; private static readonly Dictionary CustomToleranceValues = diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index c99fe8d652..cf1e49b271 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -196,6 +196,7 @@ public static class Fuzz public const string ArgumentException826B = "Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg"; public const string ArgumentException826C = "Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg"; public const string AccessViolationException827 = "Jpg/issues/fuzz/Issue827-AccessViolationException.jpg"; + public const string ExecutionEngineException839 = "Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg"; } } diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg new file mode 100644 index 0000000000..0e284349e3 Binary files /dev/null and b/tests/Images/Input/Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg differ