Browse Source

Merge branch 'master' into read-xmp-from-webp

pull/1918/head
James Jackson-South 4 years ago
committed by GitHub
parent
commit
8630d19c82
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 390
      .github/workflows/build-and-test.yml
  2. 81
      .github/workflows/code-coverage.yml
  3. 1
      ImageSharp.sln
  4. 30
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
  5. 76
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
  6. 68
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

390
.github/workflows/build-and-test.yml

@ -1,202 +1,196 @@
name: Build name: Build
on: on:
push: push:
branches: branches:
- master - master
tags: tags:
- "v*" - "v*"
pull_request: pull_request:
branches: branches:
- master - master
jobs: jobs:
Build: Build:
strategy: strategy:
matrix: matrix:
options: options:
- os: ubuntu-latest - os: ubuntu-latest
framework: net6.0 framework: net6.0
sdk: 6.0.x sdk: 6.0.x
sdk-preview: true sdk-preview: true
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: macos-latest - os: macos-latest
framework: net6.0 framework: net6.0
sdk: 6.0.x sdk: 6.0.x
sdk-preview: true sdk-preview: true
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: windows-latest - os: windows-latest
framework: net6.0 framework: net6.0
sdk: 6.0.x sdk: 6.0.x
sdk-preview: true sdk-preview: true
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: ubuntu-latest - os: ubuntu-latest
framework: net5.0 framework: net5.0
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: macos-latest - os: macos-latest
framework: net5.0 framework: net5.0
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: windows-latest - os: windows-latest
framework: net5.0 framework: net5.0
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: ubuntu-latest - os: ubuntu-latest
framework: netcoreapp3.1 framework: netcoreapp3.1
runtime: -x64 runtime: -x64
codecov: true codecov: false
- os: macos-latest - os: macos-latest
framework: netcoreapp3.1 framework: netcoreapp3.1
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: windows-latest - os: windows-latest
framework: netcoreapp3.1 framework: netcoreapp3.1
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: windows-latest - os: windows-latest
framework: netcoreapp2.1 framework: netcoreapp2.1
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: windows-latest - os: windows-latest
framework: net472 framework: net472
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: windows-latest - os: windows-latest
framework: net472 framework: net472
runtime: -x86 runtime: -x86
codecov: false codecov: false
runs-on: ${{matrix.options.os}} runs-on: ${{matrix.options.os}}
steps: steps:
- name: Git Config - name: Git Config
shell: bash shell: bash
run: | run: |
git config --global core.autocrlf false git config --global core.autocrlf false
git config --global core.longpaths true git config --global core.longpaths true
- name: Git Checkout - name: Git Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: recursive submodules: recursive
# See https://github.com/actions/checkout/issues/165#issuecomment-657673315 # See https://github.com/actions/checkout/issues/165#issuecomment-657673315
- name: Git Create LFS FileList - name: Git Create LFS FileList
run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id
- name: Git Setup LFS Cache - name: Git Setup LFS Cache
uses: actions/cache@v2 uses: actions/cache@v2
id: lfs-cache id: lfs-cache
with: with:
path: .git/lfs path: .git/lfs
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1 key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1
- name: Git Pull LFS - name: Git Pull LFS
run: git lfs pull run: git lfs pull
- name: NuGet Install - name: NuGet Install
uses: NuGet/setup-nuget@v1 uses: NuGet/setup-nuget@v1
- name: NuGet Setup Cache - name: NuGet Setup Cache
uses: actions/cache@v2 uses: actions/cache@v2
id: nuget-cache id: nuget-cache
with: with:
path: ~/.nuget path: ~/.nuget
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
restore-keys: ${{ runner.os }}-nuget- restore-keys: ${{ runner.os }}-nuget-
- name: DotNet Setup Preview - name: DotNet Setup Preview
if: ${{ matrix.options.sdk-preview == true }} if: ${{ matrix.options.sdk-preview == true }}
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: ${{ matrix.options.sdk }} dotnet-version: ${{ matrix.options.sdk }}
include-prerelease: true include-prerelease: true
- name: DotNet Build - name: DotNet Build
if: ${{ matrix.options.sdk-preview != true }} if: ${{ matrix.options.sdk-preview != true }}
shell: pwsh shell: pwsh
run: ./ci-build.ps1 "${{matrix.options.framework}}" run: ./ci-build.ps1 "${{matrix.options.framework}}"
env: env:
SIXLABORS_TESTING: True SIXLABORS_TESTING: True
- name: DotNet Build Preview - name: DotNet Build Preview
if: ${{ matrix.options.sdk-preview == true }} if: ${{ matrix.options.sdk-preview == true }}
shell: pwsh shell: pwsh
run: ./ci-build.ps1 "${{matrix.options.framework}}" run: ./ci-build.ps1 "${{matrix.options.framework}}"
env: env:
SIXLABORS_TESTING_PREVIEW: True SIXLABORS_TESTING_PREVIEW: True
- name: DotNet Test - name: DotNet Test
if: ${{ matrix.options.sdk-preview != true }} if: ${{ matrix.options.sdk-preview != true }}
shell: pwsh shell: pwsh
run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}" run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}"
env: env:
SIXLABORS_TESTING: True SIXLABORS_TESTING: True
XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
- name: DotNet Test Preview - name: DotNet Test Preview
if: ${{ matrix.options.sdk-preview == true }} if: ${{ matrix.options.sdk-preview == true }}
shell: pwsh shell: pwsh
run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}" run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}"
env: env:
SIXLABORS_TESTING_PREVIEW: True SIXLABORS_TESTING_PREVIEW: True
XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
- name: Export Failed Output - name: Export Failed Output
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
if: failure() if: failure()
with: with:
name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip
path: tests/Images/ActualOutput/ path: tests/Images/ActualOutput/
- name: Codecov Update Publish:
uses: codecov/codecov-action@v1 needs: [Build]
if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors')
with: runs-on: ubuntu-latest
flags: unittests
if: (github.event_name == 'push')
Publish:
needs: [Build] steps:
- name: Git Config
runs-on: ubuntu-latest shell: bash
run: |
if: (github.event_name == 'push') git config --global core.autocrlf false
git config --global core.longpaths true
steps:
- name: Git Config - name: Git Checkout
shell: bash uses: actions/checkout@v2
run: | with:
git config --global core.autocrlf false fetch-depth: 0
git config --global core.longpaths true submodules: recursive
- name: Git Checkout - name: NuGet Install
uses: actions/checkout@v2 uses: NuGet/setup-nuget@v1
with:
fetch-depth: 0 - name: NuGet Setup Cache
submodules: recursive uses: actions/cache@v2
id: nuget-cache
- name: NuGet Install with:
uses: NuGet/setup-nuget@v1 path: ~/.nuget
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
- name: NuGet Setup Cache restore-keys: ${{ runner.os }}-nuget-
uses: actions/cache@v2
id: nuget-cache - name: DotNet Pack
with: shell: pwsh
path: ~/.nuget run: ./ci-pack.ps1
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
restore-keys: ${{ runner.os }}-nuget- - name: MyGet Publish
shell: pwsh
- name: DotNet Pack run: |
shell: pwsh dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v2/package
run: ./ci-pack.ps1 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
- 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

81
.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

1
ImageSharp.sln

@ -551,6 +551,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{C0D7754B-5277-438E-ABEB-2BA34401B5A7}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{C0D7754B-5277-438E-ABEB-2BA34401B5A7}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.github\workflows\build-and-test.yml = .github\workflows\build-and-test.yml .github\workflows\build-and-test.yml = .github\workflows\build-and-test.yml
.github\workflows\code-coverage.yml = .github\workflows\code-coverage.yml
EndProjectSection EndProjectSection
EndProject EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SharedInfrastructure", "shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.shproj", "{68A8CC40-6AED-4E96-B524-31B1158FDEEA}" Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SharedInfrastructure", "shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.shproj", "{68A8CC40-6AED-4E96-B524-31B1158FDEEA}"

30
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs

@ -167,18 +167,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcusPerLine = this.frame.McusPerLine; int mcusPerLine = this.frame.McusPerLine;
ref HuffmanScanBuffer buffer = ref this.scanBuffer; 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++) for (int j = 0; j < mcusPerColumn; j++)
{ {
this.cancellationToken.ThrowIfCancellationRequested(); this.cancellationToken.ThrowIfCancellationRequested();
@ -248,8 +236,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
dcHuffmanTable.Configure();
acHuffmanTable.Configure();
for (int j = 0; j < h; j++) for (int j = 0; j < h; j++)
{ {
@ -347,15 +333,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcusPerLine = this.frame.McusPerLine; int mcusPerLine = this.frame.McusPerLine;
ref HuffmanScanBuffer buffer = ref this.scanBuffer; 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 j = 0; j < mcusPerColumn; j++)
{ {
for (int i = 0; i < mcusPerLine; i++) for (int i = 0; i < mcusPerLine; i++)
@ -416,7 +393,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
if (this.SpectralStart == 0) if (this.SpectralStart == 0)
{ {
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
dcHuffmanTable.Configure();
for (int j = 0; j < h; j++) for (int j = 0; j < h; j++)
{ {
@ -444,7 +420,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
else else
{ {
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
acHuffmanTable.Configure();
for (int j = 0; j < h; j++) for (int j = 0; j < h; j++)
{ {
@ -752,11 +727,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <param name="index">Table index.</param> /// <param name="index">Table index.</param>
/// <param name="codeLengths">Code lengths.</param> /// <param name="codeLengths">Code lengths.</param>
/// <param name="values">Code values.</param> /// <param name="values">Code values.</param>
/// <param name="workspace">The provided spare workspace memory, can be dirty.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void BuildHuffmanTable(int type, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values) public void BuildHuffmanTable(int type, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values, Span<uint> workspace)
{ {
HuffmanTable[] tables = type == 0 ? this.dcHuffmanTables : this.acHuffmanTables; HuffmanTable[] tables = type == 0 ? this.dcHuffmanTables : this.acHuffmanTables;
tables[index] = new HuffmanTable(codeLengths, values); tables[index] = new HuffmanTable(codeLengths, values, workspace);
} }
} }
} }

76
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs

@ -13,12 +13,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal unsafe struct HuffmanTable internal unsafe struct HuffmanTable
{ {
private bool isConfigured;
/// <summary> /// <summary>
/// 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 <see cref="HuffmanTable"/> ctor.
/// </summary> /// </summary>
public fixed byte Sizes[17]; public const int WorkspaceByteSize = 256 * sizeof(uint);
/// <summary> /// <summary>
/// Derived from the DHT marker. Contains the symbols, in order of incremental code length. /// 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
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HuffmanTable"/> struct. /// Initializes a new instance of the <see cref="HuffmanTable"/> struct.
/// </summary> /// </summary>
/// <param name="codeLengths">The code lengths</param> /// <param name="codeLengths">The code lengths.</param>
/// <param name="values">The huffman values</param> /// <param name="values">The huffman values.</param>
public HuffmanTable(ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values) /// <param name="workspace">The provided spare workspace memory, can be dirty.</param>
public HuffmanTable(ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values, Span<uint> 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); Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), (uint)values.Length);
}
/// <summary>
/// Expands the HuffmanTable into its readable form.
/// </summary>
public void Configure()
{
if (this.isConfigured)
{
return;
}
Span<char> huffSize = stackalloc char[257]; // Generate codes
Span<uint> huffCode = stackalloc uint[257]; uint code = 0;
int si = 1;
// Figure C.1: make table of Huffman code length for each symbol
int p = 0; int p = 0;
for (int j = 1; j <= 16; j++) for (int i = 1; i <= 16; i++)
{ {
int i = this.Sizes[j]; int count = codeLengths[i];
while (i-- != 0) for (int j = 0; j < count; j++)
{ {
huffSize[p++] = (char)j; workspace[p++] = code;
code++;
} }
}
huffSize[p] = (char)0; // 'code' is now 1 more than the last code used for codelength 'si'
// in the valid worst possible case 'code' would have the least
// Figure C.2: generate the codes themselves // significant bit set to 1, e.g. 1111(0) +1 => 1111(1)
uint code = 0; // but it must still fit in 'si' bits since no huffman code can be equal to all 1s
int si = huffSize[0]; // if last code is all ones, e.g. 1111(1), then incrementing it by 1 would yield
p = 0; // a new code which occupies one extra bit, e.g. 1111(1) +1 => (1)1111(0)
while (huffSize[p] != 0) if (code >= (1 << si))
{
while (huffSize[p] == si)
{ {
huffCode[p++] = code; JpegThrowHelper.ThrowInvalidImageContentException("Bad huffman table.");
code++;
} }
code <<= 1; code <<= 1;
@ -113,11 +95,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
p = 0; p = 0;
for (int j = 1; j <= 16; j++) for (int j = 1; j <= 16; j++)
{ {
if (this.Sizes[j] != 0) if (codeLengths[j] != 0)
{ {
this.ValOffset[j] = p - (int)huffCode[p]; this.ValOffset[j] = p - (int)workspace[p];
p += this.Sizes[j]; p += codeLengths[j];
this.MaxCode[j] = huffCode[p - 1]; // Maximum code of length l this.MaxCode[j] = workspace[p - 1]; // Maximum code of length l
this.MaxCode[j] <<= JpegConstants.Huffman.RegisterSize - j; // Left justify this.MaxCode[j] <<= JpegConstants.Huffman.RegisterSize - j; // Left justify
this.MaxCode[j] |= (1ul << (JpegConstants.Huffman.RegisterSize - j)) - 1; 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++) for (int length = 1; length <= JpegConstants.Huffman.LookupBits; length++)
{ {
int jShift = 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[]. // length = current code's length, p = its index in huffCode[] & Values[].
// Generate left-justified code followed by all possible bit sequences // 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--) for (int ctr = 1 << (JpegConstants.Huffman.LookupBits - length); ctr > 0; ctr--)
{ {
this.LookaheadSize[lookBits] = (byte)length; this.LookaheadSize[lookBits] = (byte)length;
@ -155,8 +137,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
} }
} }
} }
this.isConfigured = true;
} }
} }
} }

68
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);
}
} }
/// <summary> /// <summary>
@ -1153,12 +1158,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessDefineHuffmanTablesMarker(BufferedReadStream stream, int remaining) 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<byte> huffmanData = this.Configuration.MemoryAllocator.Allocate<byte>(256, AllocationOptions.Clean)) int length = remaining;
using (IMemoryOwner<byte> buffer = this.Configuration.MemoryAllocator.Allocate<byte>(totalBufferSize))
{ {
Span<byte> huffmanDataSpan = huffmanData.GetSpan(); Span<byte> bufferSpan = buffer.GetSpan();
ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanDataSpan); Span<byte> huffmanLegthsSpan = bufferSpan.Slice(0, codeLengthsByteSize);
Span<byte> huffmanValuesSpan = bufferSpan.Slice(codeLengthsByteSize, codeValuesMaxByteSize);
Span<uint> tableWorkspace = MemoryMarshal.Cast<byte, uint>(bufferSpan.Slice(codeLengthsByteSize + codeValuesMaxByteSize));
for (int i = 2; i < remaining;) for (int i = 2; i < remaining;)
{ {
byte huffmanTableSpec = (byte)stream.ReadByte(); byte huffmanTableSpec = (byte)stream.ReadByte();
@ -1168,49 +1179,40 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Types 0..1 DC..AC // Types 0..1 DC..AC
if (tableType > 1) if (tableType > 1)
{ {
JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table type: {tableType}"); JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table type: {tableType}.");
} }
// Max tables of each type // Max tables of each type
if (tableIndex > 3) 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<byte> codeLengths = this.Configuration.MemoryAllocator.Allocate<byte>(17, AllocationOptions.Clean)) int codeLengthSum = 0;
for (int j = 1; j < 17; j++)
{ {
Span<byte> codeLengthsSpan = codeLengths.GetSpan(); codeLengthSum += huffmanLegthsSpan[j];
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);
}
length -= 17; length -= 17;
if (codeLengthSum > 256 || codeLengthSum > length) if (codeLengthSum > 256 || codeLengthSum > length)
{ {
JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length."); JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length.");
} }
using (IMemoryOwner<byte> huffmanValues = this.Configuration.MemoryAllocator.Allocate<byte>(256, AllocationOptions.Clean)) stream.Read(huffmanValuesSpan, 0, codeLengthSum);
{
Span<byte> huffmanValuesSpan = huffmanValues.GetSpan();
stream.Read(huffmanValuesSpan, 0, codeLengthSum);
i += 17 + codeLengthSum; i += 17 + codeLengthSum;
this.scanDecoder.BuildHuffmanTable( this.scanDecoder.BuildHuffmanTable(
tableType, tableType,
tableIndex, tableIndex,
codeLengthsSpan, huffmanLegthsSpan,
huffmanValuesSpan); huffmanValuesSpan.Slice(0, codeLengthSum),
} tableWorkspace);
}
} }
} }
} }

Loading…
Cancel
Save