Browse Source

Lazily derive the huffman tables. Fix #839 (#841)

* Lazily derive the huffman tables. Fix #839

* Lazy invoke fast table

* Add performance tweaks to scan decoder.

* Remove unneccessary classes.
af/merge-core
James Jackson-South 7 years ago
committed by GitHub
parent
commit
a69eb5bbed
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 61
      src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs
  2. 90
      src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs
  3. 31
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
  4. 26
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs
  5. 164
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
  6. 22
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  7. 3
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
  8. 1
      tests/ImageSharp.Tests/TestImages.cs
  9. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg

61
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
{
/// <summary>
/// Gets the lookahead array
/// </summary>
public fixed short Lookahead[512];
/// <summary>
/// Derives a lookup table for fast AC entropy scan decoding.
/// This can happen multiple times during progressive decoding but always outside mcu loops.
/// </summary>
/// <param name="huffmanTable">The AC Huffman table.</param>
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));
}
}
}
}
}
}
}

90
src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs

@ -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
{
/// <summary>
/// The collection of lookup tables used for fast AC entropy scan decoding.
/// </summary>
internal sealed class FastACTables : IDisposable
{
private Buffer2D<short> tables;
/// <summary>
/// Initializes a new instance of the <see cref="FastACTables"/> class.
/// </summary>
/// <param name="memoryAllocator">The memory allocator used to allocate memory for image processing operations.</param>
public FastACTables(MemoryAllocator memoryAllocator) => this.tables = memoryAllocator.Allocate2D<short>(512, 4, AllocationOptions.Clean);
/// <summary>
/// Gets the <see cref="Span{Int16}"/> representing the table at the index in the collection.
/// </summary>
/// <param name="index">The table index.</param>
/// <returns><see cref="Span{Int16}"/></returns>
[MethodImpl(InliningOptions.ShortMethod)]
public ReadOnlySpan<short> GetTableSpan(int index) => this.tables.GetRowSpan(index);
/// <summary>
/// Gets a reference to the first element of the AC table indexed by <see cref="JpegComponent.ACHuffmanTableId"/>
/// </summary>
/// <param name="component">The frame component.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public ref short GetAcTableReference(JpegComponent component) => ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0];
/// <summary>
/// Builds a lookup table for fast AC entropy scan decoding.
/// </summary>
/// <param name="index">The table index.</param>
/// <param name="acHuffmanTables">The collection of AC Huffman tables.</param>
public unsafe void BuildACTableLut(int index, HuffmanTables acHuffmanTables)
{
const int FastBits = ScanDecoder.FastBits;
Span<short> 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));
}
}
}
}
}
/// <inheritdoc />
public void Dispose()
{
this.tables?.Dispose();
this.tables = null;
}
}
}

31
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
/// <summary>
/// Gets the max code array
/// </summary>
@ -50,16 +58,31 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <param name="values">The huffman values</param>
public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> 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);
}
/// <summary>
/// Expands the HuffmanTable into its derived form.
/// </summary>
public void Derive()
{
if (this.isDerived)
{
return;
}
const int Length = 257;
using (IMemoryOwner<short> huffcode = memoryAllocator.Allocate<short>(Length))
using (IMemoryOwner<short> huffcode = this.memoryAllocator.Allocate<short>(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 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), 256);
this.isDerived = true;
}
}
}

26
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs

@ -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
{
/// <summary>
/// Defines a 2 pairs of huffman tables.
/// </summary>
internal sealed class HuffmanTables
{
private readonly HuffmanTable[] tables = new HuffmanTable[4];
/// <summary>
/// Gets or sets the table at the given index.
/// </summary>
/// <param name="index">The index</param>
/// <returns>The <see cref="HuffmanTable"/></returns>
public ref HuffmanTable this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref this.tables[index];
}
}
}

164
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 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
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 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
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 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
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 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
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<Block8x8> 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 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
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 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// number of blocks to do just depends on how many actual "pixels" each component has,
/// independent of interleaved MCU blocking and such.
/// </summary>
private void ParseBaselineDataNonInterleaved()
private unsafe void ParseBaselineDataNonInterleaved()
{
JpegComponent component = this.components[this.frame.ComponentOrder[0]];
@ -234,14 +254,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
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<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
int blockRow = j;
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(j);
ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
for (int i = 0; i < w; i++)
{
@ -250,12 +275,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
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 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
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 @@ 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<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
for (int x = 0; x < h; x++)
{
@ -368,12 +403,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
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 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// number of blocks to do just depends on how many actual "pixels" this
/// component has, independent of interleaved MCU blocking and such
/// </summary>
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<Block8x8> 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<Block8x8> 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<Block8x8> 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;
}
}
}
}

22
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -52,17 +52,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary>
/// The DC Huffman tables
/// </summary>
private HuffmanTables dcHuffmanTables;
private HuffmanTable[] dcHuffmanTables;
/// <summary>
/// The AC Huffman tables
/// </summary>
private HuffmanTables acHuffmanTables;
private HuffmanTable[] acHuffmanTables;
/// <summary>
/// The fast AC tables used for entropy decoding
/// </summary>
private FastACTables fastACTables;
private FastACTable[] fastACTables;
/// <summary>
/// The reset interval determined by RST markers
@ -266,9 +266,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// 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 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
this.InputStream?.Dispose();
this.Frame?.Dispose();
this.fastACTables?.Dispose();
// Set large fields to null.
this.InputStream = null;
@ -872,12 +872,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
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 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="codeLengths">The codelengths</param>
/// <param name="values">The values</param>
[MethodImpl(InliningOptions.ShortMethod)]
private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values)
private void BuildHuffmanTable(HuffmanTable[] tables, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values)
=> tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values);
/// <summary>

3
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs

@ -79,7 +79,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
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<string, float> CustomToleranceValues =

1
tests/ImageSharp.Tests/TestImages.cs

@ -196,6 +196,7 @@ namespace SixLabors.ImageSharp.Tests
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";
}
}

3
tests/Images/Input/Jpg/issues/fuzz/Issue839-ExecutionEngineException.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b37ac1cc34680db9721542ef5354f7509860afd37888a24f6350f29f5b63eea1
size 397
Loading…
Cancel
Save