diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 5189f04356..6d31e8c532 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -1,202 +1,196 @@
name: Build
on:
- push:
- branches:
- - master
- tags:
- - "v*"
- pull_request:
- branches:
- - master
+ push:
+ branches:
+ - master
+ tags:
+ - "v*"
+ pull_request:
+ branches:
+ - master
jobs:
- Build:
- strategy:
- matrix:
- options:
- - os: ubuntu-latest
- framework: net6.0
- sdk: 6.0.x
- sdk-preview: true
- runtime: -x64
- codecov: false
- - os: macos-latest
- framework: net6.0
- sdk: 6.0.x
- sdk-preview: true
- runtime: -x64
- codecov: false
- - os: windows-latest
- framework: net6.0
- sdk: 6.0.x
- sdk-preview: true
- runtime: -x64
- codecov: false
- - os: ubuntu-latest
- framework: net5.0
- runtime: -x64
- codecov: false
- - os: macos-latest
- framework: net5.0
- runtime: -x64
- codecov: false
- - os: windows-latest
- framework: net5.0
- runtime: -x64
- codecov: false
- - os: ubuntu-latest
- framework: netcoreapp3.1
- runtime: -x64
- codecov: true
- - os: macos-latest
- framework: netcoreapp3.1
- runtime: -x64
- codecov: false
- - os: windows-latest
- framework: netcoreapp3.1
- runtime: -x64
- codecov: false
- - os: windows-latest
- framework: netcoreapp2.1
- runtime: -x64
- codecov: false
- - os: windows-latest
- framework: net472
- runtime: -x64
- codecov: false
- - os: windows-latest
- framework: net472
- runtime: -x86
- codecov: false
-
- runs-on: ${{matrix.options.os}}
-
- steps:
- - name: Git Config
- shell: bash
- run: |
- git config --global core.autocrlf false
- git config --global core.longpaths true
-
- - name: Git Checkout
- uses: actions/checkout@v2
- with:
- fetch-depth: 0
- submodules: recursive
-
- # See https://github.com/actions/checkout/issues/165#issuecomment-657673315
- - name: Git Create LFS FileList
- run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id
-
- - name: Git Setup LFS Cache
- uses: actions/cache@v2
- id: lfs-cache
- with:
- path: .git/lfs
- key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1
-
- - name: Git Pull LFS
- run: git lfs pull
-
- - name: NuGet Install
- uses: NuGet/setup-nuget@v1
-
- - name: NuGet Setup Cache
- uses: actions/cache@v2
- id: nuget-cache
- with:
- path: ~/.nuget
- key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
- restore-keys: ${{ runner.os }}-nuget-
-
- - name: DotNet Setup Preview
- if: ${{ matrix.options.sdk-preview == true }}
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: ${{ matrix.options.sdk }}
- include-prerelease: true
-
- - name: DotNet Build
- if: ${{ matrix.options.sdk-preview != true }}
- shell: pwsh
- run: ./ci-build.ps1 "${{matrix.options.framework}}"
- env:
- SIXLABORS_TESTING: True
-
- - name: DotNet Build Preview
- if: ${{ matrix.options.sdk-preview == true }}
- shell: pwsh
- run: ./ci-build.ps1 "${{matrix.options.framework}}"
- env:
- SIXLABORS_TESTING_PREVIEW: True
-
- - name: DotNet Test
- if: ${{ matrix.options.sdk-preview != true }}
- shell: pwsh
- run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}"
- env:
- SIXLABORS_TESTING: True
- XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
-
- - name: DotNet Test Preview
- if: ${{ matrix.options.sdk-preview == true }}
- shell: pwsh
- run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}"
- env:
- SIXLABORS_TESTING_PREVIEW: True
- XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
-
- - name: Export Failed Output
- uses: actions/upload-artifact@v2
- if: failure()
- with:
- name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip
- path: tests/Images/ActualOutput/
-
- - name: Codecov Update
- uses: codecov/codecov-action@v1
- if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors')
- with:
- flags: unittests
-
- Publish:
- needs: [Build]
-
- runs-on: ubuntu-latest
-
- if: (github.event_name == 'push')
-
- steps:
- - name: Git Config
- shell: bash
- run: |
- git config --global core.autocrlf false
- git config --global core.longpaths true
-
- - name: Git Checkout
- uses: actions/checkout@v2
- with:
- fetch-depth: 0
- submodules: recursive
-
- - name: NuGet Install
- uses: NuGet/setup-nuget@v1
-
- - name: NuGet Setup Cache
- uses: actions/cache@v2
- id: nuget-cache
- with:
- path: ~/.nuget
- key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
- restore-keys: ${{ runner.os }}-nuget-
-
- - name: DotNet Pack
- shell: pwsh
- run: ./ci-pack.ps1
-
- - name: MyGet Publish
- shell: pwsh
- run: |
- dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v2/package
- dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v3/index.json
- # TODO: If github.ref starts with 'refs/tags' then it was tag push and we can optionally push out package to nuget.org
+ Build:
+ strategy:
+ matrix:
+ options:
+ - os: ubuntu-latest
+ framework: net6.0
+ sdk: 6.0.x
+ sdk-preview: true
+ runtime: -x64
+ codecov: false
+ - os: macos-latest
+ framework: net6.0
+ sdk: 6.0.x
+ sdk-preview: true
+ runtime: -x64
+ codecov: false
+ - os: windows-latest
+ framework: net6.0
+ sdk: 6.0.x
+ sdk-preview: true
+ runtime: -x64
+ codecov: false
+ - os: ubuntu-latest
+ framework: net5.0
+ runtime: -x64
+ codecov: false
+ - os: macos-latest
+ framework: net5.0
+ runtime: -x64
+ codecov: false
+ - os: windows-latest
+ framework: net5.0
+ runtime: -x64
+ codecov: false
+ - os: ubuntu-latest
+ framework: netcoreapp3.1
+ runtime: -x64
+ codecov: false
+ - os: macos-latest
+ framework: netcoreapp3.1
+ runtime: -x64
+ codecov: false
+ - os: windows-latest
+ framework: netcoreapp3.1
+ runtime: -x64
+ codecov: false
+ - os: windows-latest
+ framework: netcoreapp2.1
+ runtime: -x64
+ codecov: false
+ - os: windows-latest
+ framework: net472
+ runtime: -x64
+ codecov: false
+ - os: windows-latest
+ framework: net472
+ runtime: -x86
+ codecov: false
+
+ runs-on: ${{matrix.options.os}}
+
+ steps:
+ - name: Git Config
+ shell: bash
+ run: |
+ git config --global core.autocrlf false
+ git config --global core.longpaths true
+
+ - name: Git Checkout
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ # See https://github.com/actions/checkout/issues/165#issuecomment-657673315
+ - name: Git Create LFS FileList
+ run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id
+
+ - name: Git Setup LFS Cache
+ uses: actions/cache@v2
+ id: lfs-cache
+ with:
+ path: .git/lfs
+ key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1
+
+ - name: Git Pull LFS
+ run: git lfs pull
+
+ - name: NuGet Install
+ uses: NuGet/setup-nuget@v1
+
+ - name: NuGet Setup Cache
+ uses: actions/cache@v2
+ id: nuget-cache
+ with:
+ path: ~/.nuget
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
+ restore-keys: ${{ runner.os }}-nuget-
+
+ - name: DotNet Setup Preview
+ if: ${{ matrix.options.sdk-preview == true }}
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: ${{ matrix.options.sdk }}
+ include-prerelease: true
+
+ - name: DotNet Build
+ if: ${{ matrix.options.sdk-preview != true }}
+ shell: pwsh
+ run: ./ci-build.ps1 "${{matrix.options.framework}}"
+ env:
+ SIXLABORS_TESTING: True
+
+ - name: DotNet Build Preview
+ if: ${{ matrix.options.sdk-preview == true }}
+ shell: pwsh
+ run: ./ci-build.ps1 "${{matrix.options.framework}}"
+ env:
+ SIXLABORS_TESTING_PREVIEW: True
+
+ - name: DotNet Test
+ if: ${{ matrix.options.sdk-preview != true }}
+ shell: pwsh
+ run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}"
+ env:
+ SIXLABORS_TESTING: True
+ XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
+
+ - name: DotNet Test Preview
+ if: ${{ matrix.options.sdk-preview == true }}
+ shell: pwsh
+ run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}"
+ env:
+ SIXLABORS_TESTING_PREVIEW: True
+ XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
+
+ - name: Export Failed Output
+ uses: actions/upload-artifact@v2
+ if: failure()
+ with:
+ name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip
+ path: tests/Images/ActualOutput/
+
+ Publish:
+ needs: [Build]
+
+ runs-on: ubuntu-latest
+
+ if: (github.event_name == 'push')
+
+ steps:
+ - name: Git Config
+ shell: bash
+ run: |
+ git config --global core.autocrlf false
+ git config --global core.longpaths true
+
+ - name: Git Checkout
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: NuGet Install
+ uses: NuGet/setup-nuget@v1
+
+ - name: NuGet Setup Cache
+ uses: actions/cache@v2
+ id: nuget-cache
+ with:
+ path: ~/.nuget
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
+ restore-keys: ${{ runner.os }}-nuget-
+
+ - name: DotNet Pack
+ shell: pwsh
+ run: ./ci-pack.ps1
+
+ - name: MyGet Publish
+ shell: pwsh
+ run: |
+ dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v2/package
+ dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v3/index.json
+ # TODO: If github.ref starts with 'refs/tags' then it was tag push and we can optionally push out package to nuget.org
diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
new file mode 100644
index 0000000000..2b14f2a4b7
--- /dev/null
+++ b/.github/workflows/code-coverage.yml
@@ -0,0 +1,81 @@
+name: CodeCoverage
+
+on:
+ schedule:
+ # 2AM every Tuesday/Thursday
+ - cron: "0 2 * * 2,4"
+jobs:
+ Build:
+ strategy:
+ matrix:
+ options:
+ - os: ubuntu-latest
+ framework: netcoreapp3.1
+ runtime: -x64
+ codecov: true
+
+ runs-on: ${{matrix.options.os}}
+
+ steps:
+ - name: Git Config
+ shell: bash
+ run: |
+ git config --global core.autocrlf false
+ git config --global core.longpaths true
+
+ - name: Git Checkout
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ # See https://github.com/actions/checkout/issues/165#issuecomment-657673315
+ - name: Git Create LFS FileList
+ run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id
+
+ - name: Git Setup LFS Cache
+ uses: actions/cache@v2
+ id: lfs-cache
+ with:
+ path: .git/lfs
+ key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1
+
+ - name: Git Pull LFS
+ run: git lfs pull
+
+ - name: NuGet Install
+ uses: NuGet/setup-nuget@v1
+
+ - name: NuGet Setup Cache
+ uses: actions/cache@v2
+ id: nuget-cache
+ with:
+ path: ~/.nuget
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
+ restore-keys: ${{ runner.os }}-nuget-
+
+ - name: DotNet Build
+ shell: pwsh
+ run: ./ci-build.ps1 "${{matrix.options.framework}}"
+ env:
+ SIXLABORS_TESTING: True
+
+ - name: DotNet Test
+ shell: pwsh
+ run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}"
+ env:
+ SIXLABORS_TESTING: True
+ XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
+
+ - name: Export Failed Output
+ uses: actions/upload-artifact@v2
+ if: failure()
+ with:
+ name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip
+ path: tests/Images/ActualOutput/
+
+ - name: Codecov Update
+ uses: codecov/codecov-action@v1
+ if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors')
+ with:
+ flags: unittests
diff --git a/ImageSharp.sln b/ImageSharp.sln
index f16f98ac59..17d293b434 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -551,6 +551,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{C0D7754B-5277-438E-ABEB-2BA34401B5A7}"
ProjectSection(SolutionItems) = preProject
.github\workflows\build-and-test.yml = .github\workflows\build-and-test.yml
+ .github\workflows\code-coverage.yml = .github\workflows\code-coverage.yml
EndProjectSection
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SharedInfrastructure", "shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.shproj", "{68A8CC40-6AED-4E96-B524-31B1158FDEEA}"
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
index ce5e5110b6..2ae3ae86bc 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
@@ -167,18 +167,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcusPerLine = this.frame.McusPerLine;
ref HuffmanScanBuffer buffer = ref this.scanBuffer;
- // Pre-derive the huffman table to avoid in-loop checks.
- for (int i = 0; i < this.componentsCount; 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.Configure();
- acHuffmanTable.Configure();
- }
-
for (int j = 0; j < mcusPerColumn; j++)
{
this.cancellationToken.ThrowIfCancellationRequested();
@@ -248,8 +236,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
- dcHuffmanTable.Configure();
- acHuffmanTable.Configure();
for (int j = 0; j < h; j++)
{
@@ -347,15 +333,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcusPerLine = this.frame.McusPerLine;
ref HuffmanScanBuffer buffer = ref this.scanBuffer;
- // Pre-derive the huffman table to avoid in-loop checks.
- for (int k = 0; k < this.componentsCount; k++)
- {
- int order = this.frame.ComponentOrder[k];
- JpegComponent component = this.components[order];
- ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
- dcHuffmanTable.Configure();
- }
-
for (int j = 0; j < mcusPerColumn; j++)
{
for (int i = 0; i < mcusPerLine; i++)
@@ -416,7 +393,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
if (this.SpectralStart == 0)
{
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
- dcHuffmanTable.Configure();
for (int j = 0; j < h; j++)
{
@@ -444,7 +420,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
else
{
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
- acHuffmanTable.Configure();
for (int j = 0; j < h; j++)
{
@@ -752,11 +727,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// Table index.
/// Code lengths.
/// Code values.
+ /// The provided spare workspace memory, can be dirty.
[MethodImpl(InliningOptions.ShortMethod)]
- public void BuildHuffmanTable(int type, int index, ReadOnlySpan codeLengths, ReadOnlySpan values)
+ public void BuildHuffmanTable(int type, int index, ReadOnlySpan codeLengths, ReadOnlySpan values, Span workspace)
{
HuffmanTable[] tables = type == 0 ? this.dcHuffmanTables : this.acHuffmanTables;
- tables[index] = new HuffmanTable(codeLengths, values);
+ tables[index] = new HuffmanTable(codeLengths, values, workspace);
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
index f18c636278..bee5e0229b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
@@ -13,12 +13,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct HuffmanTable
{
- private bool isConfigured;
-
///
- /// Derived from the DHT marker. Sizes[k] = # of symbols with codes of length k bits; Sizes[0] is unused.
+ /// Memory workspace buffer size used in ctor.
///
- public fixed byte Sizes[17];
+ public const int WorkspaceByteSize = 256 * sizeof(uint);
///
/// Derived from the DHT marker. Contains the symbols, in order of incremental code length.
@@ -58,51 +56,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Initializes a new instance of the struct.
///
- /// The code lengths
- /// The huffman values
- public HuffmanTable(ReadOnlySpan codeLengths, ReadOnlySpan values)
+ /// The code lengths.
+ /// The huffman values.
+ /// The provided spare workspace memory, can be dirty.
+ public HuffmanTable(ReadOnlySpan codeLengths, ReadOnlySpan values, Span workspace)
{
- this.isConfigured = false;
- Unsafe.CopyBlockUnaligned(ref this.Sizes[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 readable form.
- ///
- public void Configure()
- {
- if (this.isConfigured)
- {
- return;
- }
- Span huffSize = stackalloc char[257];
- Span huffCode = stackalloc uint[257];
-
- // Figure C.1: make table of Huffman code length for each symbol
+ // Generate codes
+ uint code = 0;
+ int si = 1;
int p = 0;
- for (int j = 1; j <= 16; j++)
+ for (int i = 1; i <= 16; i++)
{
- int i = this.Sizes[j];
- while (i-- != 0)
+ int count = codeLengths[i];
+ for (int j = 0; j < count; j++)
{
- huffSize[p++] = (char)j;
+ workspace[p++] = code;
+ code++;
}
- }
- huffSize[p] = (char)0;
-
- // Figure C.2: generate the codes themselves
- uint code = 0;
- int si = huffSize[0];
- p = 0;
- while (huffSize[p] != 0)
- {
- while (huffSize[p] == si)
+ // 'code' is now 1 more than the last code used for codelength 'si'
+ // in the valid worst possible case 'code' would have the least
+ // significant bit set to 1, e.g. 1111(0) +1 => 1111(1)
+ // but it must still fit in 'si' bits since no huffman code can be equal to all 1s
+ // if last code is all ones, e.g. 1111(1), then incrementing it by 1 would yield
+ // a new code which occupies one extra bit, e.g. 1111(1) +1 => (1)1111(0)
+ if (code >= (1 << si))
{
- huffCode[p++] = code;
- code++;
+ JpegThrowHelper.ThrowInvalidImageContentException("Bad huffman table.");
}
code <<= 1;
@@ -113,11 +95,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
p = 0;
for (int j = 1; j <= 16; j++)
{
- if (this.Sizes[j] != 0)
+ if (codeLengths[j] != 0)
{
- this.ValOffset[j] = p - (int)huffCode[p];
- p += this.Sizes[j];
- this.MaxCode[j] = huffCode[p - 1]; // Maximum code of length l
+ this.ValOffset[j] = p - (int)workspace[p];
+ p += codeLengths[j];
+ this.MaxCode[j] = workspace[p - 1]; // Maximum code of length l
this.MaxCode[j] <<= JpegConstants.Huffman.RegisterSize - j; // Left justify
this.MaxCode[j] |= (1ul << (JpegConstants.Huffman.RegisterSize - j)) - 1;
}
@@ -142,11 +124,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int length = 1; length <= JpegConstants.Huffman.LookupBits; length++)
{
int jShift = JpegConstants.Huffman.LookupBits - length;
- for (int i = 1; i <= this.Sizes[length]; i++, p++)
+ for (int i = 1; i <= codeLengths[length]; i++, p++)
{
// length = current code's length, p = its index in huffCode[] & Values[].
// Generate left-justified code followed by all possible bit sequences
- int lookBits = (int)(huffCode[p] << jShift);
+ int lookBits = (int)(workspace[p] << jShift);
for (int ctr = 1 << (JpegConstants.Huffman.LookupBits - length); ctr > 0; ctr--)
{
this.LookaheadSize[lookBits] = (byte)length;
@@ -155,8 +137,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
}
-
- this.isConfigured = true;
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index 18f8dcc561..6c529f0c1f 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -846,6 +846,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
}
}
+ else
+ {
+ // If the profile is unknown skip over the rest of it.
+ stream.Skip(remaining);
+ }
}
///
@@ -1153,12 +1158,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The remaining bytes in the segment block.
private void ProcessDefineHuffmanTablesMarker(BufferedReadStream stream, int remaining)
{
- int length = remaining;
+ const int codeLengthsByteSize = 17;
+ const int codeValuesMaxByteSize = 256;
+ const int totalBufferSize = codeLengthsByteSize + codeValuesMaxByteSize + HuffmanTable.WorkspaceByteSize;
- using (IMemoryOwner huffmanData = this.Configuration.MemoryAllocator.Allocate(256, AllocationOptions.Clean))
+ int length = remaining;
+ using (IMemoryOwner buffer = this.Configuration.MemoryAllocator.Allocate(totalBufferSize))
{
- Span huffmanDataSpan = huffmanData.GetSpan();
- ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanDataSpan);
+ Span bufferSpan = buffer.GetSpan();
+ Span huffmanLegthsSpan = bufferSpan.Slice(0, codeLengthsByteSize);
+ Span huffmanValuesSpan = bufferSpan.Slice(codeLengthsByteSize, codeValuesMaxByteSize);
+ Span tableWorkspace = MemoryMarshal.Cast(bufferSpan.Slice(codeLengthsByteSize + codeValuesMaxByteSize));
+
for (int i = 2; i < remaining;)
{
byte huffmanTableSpec = (byte)stream.ReadByte();
@@ -1168,49 +1179,40 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Types 0..1 DC..AC
if (tableType > 1)
{
- JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table type: {tableType}");
+ JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table type: {tableType}.");
}
// Max tables of each type
if (tableIndex > 3)
{
- JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table index: {tableIndex}");
+ JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table index: {tableIndex}.");
}
- stream.Read(huffmanDataSpan, 0, 16);
+ stream.Read(huffmanLegthsSpan, 1, 16);
- using (IMemoryOwner codeLengths = this.Configuration.MemoryAllocator.Allocate(17, AllocationOptions.Clean))
+ int codeLengthSum = 0;
+ for (int j = 1; j < 17; j++)
{
- Span codeLengthsSpan = codeLengths.GetSpan();
- ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengthsSpan);
- int codeLengthSum = 0;
-
- for (int j = 1; j < 17; j++)
- {
- codeLengthSum += Unsafe.Add(ref codeLengthsRef, j) = Unsafe.Add(ref huffmanDataRef, j - 1);
- }
+ codeLengthSum += huffmanLegthsSpan[j];
+ }
- length -= 17;
+ length -= 17;
- if (codeLengthSum > 256 || codeLengthSum > length)
- {
- JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length.");
- }
+ if (codeLengthSum > 256 || codeLengthSum > length)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length.");
+ }
- using (IMemoryOwner huffmanValues = this.Configuration.MemoryAllocator.Allocate(256, AllocationOptions.Clean))
- {
- Span huffmanValuesSpan = huffmanValues.GetSpan();
- stream.Read(huffmanValuesSpan, 0, codeLengthSum);
+ stream.Read(huffmanValuesSpan, 0, codeLengthSum);
- i += 17 + codeLengthSum;
+ i += 17 + codeLengthSum;
- this.scanDecoder.BuildHuffmanTable(
- tableType,
- tableIndex,
- codeLengthsSpan,
- huffmanValuesSpan);
- }
- }
+ this.scanDecoder.BuildHuffmanTable(
+ tableType,
+ tableIndex,
+ huffmanLegthsSpan,
+ huffmanValuesSpan.Slice(0, codeLengthSum),
+ tableWorkspace);
}
}
}