From 18edc46b0fee295151671e1e67230bf441b59827 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 11 Aug 2021 00:21:01 +0200 Subject: [PATCH 1/5] If component id's are R, G, B in ASCII the color space should be RGB --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 896e5f0aa..269b2fe76 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -345,10 +345,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } /// - /// Returns the correct colorspace based on the image component count + /// Returns the correct colorspace based on the image component count and the jpeg frame components. /// /// The - private JpegColorSpace DeduceJpegColorSpace(byte componentCount) + private JpegColorSpace DeduceJpegColorSpace(byte componentCount, JpegComponent[] components) { if (componentCount == 1) { @@ -362,6 +362,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return JpegColorSpace.RGB; } + // If the component Id's are R, G, B in ASCII the colorspace is RGB and not YCbCr. + if (components[0].Id == 82 && components[1].Id == 71 && components[2].Id == 66) + { + return JpegColorSpace.RGB; + } + // Some images are poorly encoded and contain incorrect colorspace transform metadata. // We ignore that and always fall back to the default colorspace. return JpegColorSpace.YCbCr; @@ -836,9 +842,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg // 1 byte: Number of components byte componentCount = this.temp[5]; - this.ColorSpace = this.DeduceJpegColorSpace(componentCount); - - this.Metadata.GetJpegMetadata().ColorType = this.ColorSpace == JpegColorSpace.Grayscale ? JpegColorType.Luminance : JpegColorType.YCbCr; this.Frame = new JpegFrame(frameMarker, precision, frameWidth, frameHeight, componentCount); @@ -888,6 +891,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg index += componentBytes; } + this.ColorSpace = this.DeduceJpegColorSpace(componentCount, this.Frame.Components); + this.Metadata.GetJpegMetadata().ColorType = this.ColorSpace == JpegColorSpace.Grayscale ? JpegColorType.Luminance : JpegColorType.YCbCr; + this.Frame.Init(maxH, maxV); this.scanDecoder.InjectFrameData(this.Frame, this); From 6cb871711762f9327b4deed88ab0e5a85fdfd5aa Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 11 Aug 2021 00:36:09 +0200 Subject: [PATCH 2/5] Add unit test for issue #1732 --- .../Formats/Jpg/JpegDecoderTests.cs | 13 +++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + .../Input/Jpg/issues/Issue1732-WrongColorSpace.jpg | 3 +++ 3 files changed, 17 insertions(+) create mode 100644 tests/Images/Input/Jpg/issues/Issue1732-WrongColorSpace.jpg diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 674aa6d8f..e8d307f90 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -174,6 +174,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg await Assert.ThrowsAsync(async () => await Image.IdentifyAsync(config, "someFakeFile", cts.Token)); } + // https://github.com/SixLabors/ImageSharp/pull/1732 + [Theory] + [WithFile(TestImages.Jpeg.Issues.WrongColorSpace, PixelTypes.Rgba32)] + public void Issue1732_DecodesWithRgbColorSpace(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(new JpegDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + // DEBUG ONLY! // The PDF.js output should be saved by "tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm" // into "\tests\Images\ActualOutput\JpegDecoderTests\" diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 059e51cec..8b9aa1a5f 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -237,6 +237,7 @@ namespace SixLabors.ImageSharp.Tests public const string ExifResize1049 = "Jpg/issues/issue1049-exif-resize.jpg"; public const string BadSubSampling1076 = "Jpg/issues/issue-1076-invalid-subsampling.jpg"; public const string IdentifyMultiFrame1211 = "Jpg/issues/issue-1221-identify-multi-frame.jpg"; + public const string WrongColorSpace = "Jpg/issues/Issue1732-WrongColorSpace.jpg"; public static class Fuzz { diff --git a/tests/Images/Input/Jpg/issues/Issue1732-WrongColorSpace.jpg b/tests/Images/Input/Jpg/issues/Issue1732-WrongColorSpace.jpg new file mode 100644 index 000000000..e3ba85ae8 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue1732-WrongColorSpace.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c72235954cdfb9d0cc7f09c537704e617313dc77708b4dca27b47c94c5e67a6 +size 2852 From 68a706fb06f576bcf2c0e1dfb038710b9f732753 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 11 Aug 2021 01:05:14 +0200 Subject: [PATCH 3/5] Move reading frame ComponentIds out of only metadata block --- .../Formats/Jpeg/JpegDecoderCore.cs | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 269b2fe76..413c9b2bd 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -845,57 +845,57 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.Frame = new JpegFrame(frameMarker, precision, frameWidth, frameHeight, componentCount); - if (!metadataOnly) + remaining -= length; + + // Validate: remaining part must be equal to components * 3 + const int componentBytes = 3; + if (remaining != componentCount * componentBytes) { - remaining -= length; + JpegThrowHelper.ThrowBadMarker("SOFn", remaining); + } - // Validate: remaining part must be equal to components * 3 - const int componentBytes = 3; - if (remaining != componentCount * componentBytes) - { - JpegThrowHelper.ThrowBadMarker("SOFn", remaining); - } + // components*3 bytes: component data + stream.Read(this.temp, 0, remaining); - // components*3 bytes: component data - stream.Read(this.temp, 0, remaining); + // No need to pool this. They max out at 4 + this.Frame.ComponentIds = new byte[componentCount]; + this.Frame.ComponentOrder = new byte[componentCount]; + this.Frame.Components = new JpegComponent[componentCount]; - // No need to pool this. They max out at 4 - this.Frame.ComponentIds = new byte[componentCount]; - this.Frame.ComponentOrder = new byte[componentCount]; - this.Frame.Components = new JpegComponent[componentCount]; + int maxH = 0; + int maxV = 0; + int index = 0; + for (int i = 0; i < componentCount; i++) + { + byte hv = this.temp[index + 1]; + int h = (hv >> 4) & 15; + int v = hv & 15; - int maxH = 0; - int maxV = 0; - int index = 0; - for (int i = 0; i < componentCount; i++) + if (maxH < h) { - byte hv = this.temp[index + 1]; - int h = (hv >> 4) & 15; - int v = hv & 15; + maxH = h; + } - if (maxH < h) - { - maxH = h; - } + if (maxV < v) + { + maxV = v; + } - if (maxV < v) - { - maxV = v; - } + var component = new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); - var component = new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); + this.Frame.Components[i] = component; + this.Frame.ComponentIds[i] = component.Id; - this.Frame.Components[i] = component; - this.Frame.ComponentIds[i] = component.Id; + index += componentBytes; + } - index += componentBytes; - } + this.ColorSpace = this.DeduceJpegColorSpace(componentCount, this.Frame.Components); - this.ColorSpace = this.DeduceJpegColorSpace(componentCount, this.Frame.Components); - this.Metadata.GetJpegMetadata().ColorType = this.ColorSpace == JpegColorSpace.Grayscale ? JpegColorType.Luminance : JpegColorType.YCbCr; + this.Metadata.GetJpegMetadata().ColorType = this.ColorSpace == JpegColorSpace.Grayscale ? JpegColorType.Luminance : JpegColorType.YCbCr; + if (!metadataOnly) + { this.Frame.Init(maxH, maxV); - this.scanDecoder.InjectFrameData(this.Frame, this); } } From 7e7dbbb94317efa449a270de47d67879528feb72 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 13 Aug 2021 16:15:23 +0200 Subject: [PATCH 4/5] Switch order of component id check --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 413c9b2bd..e94b07faa 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -363,7 +363,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } // If the component Id's are R, G, B in ASCII the colorspace is RGB and not YCbCr. - if (components[0].Id == 82 && components[1].Id == 71 && components[2].Id == 66) + if (components[2].Id == 66 && components[1].Id == 71 && components[0].Id == 82) { return JpegColorSpace.RGB; } From 36f4ad48f903c8c1f3f2b867e1975d9f3d1d1f39 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 22 Aug 2021 21:46:59 +1000 Subject: [PATCH 5/5] Enable .NET 6 Preview Builds (#1745) Enable Net 6 and C#9 --- .github/workflows/build-and-test.yml | 141 ++++++++++++------ Directory.Build.props | 5 + shared-infrastructure | 2 +- src/ImageSharp/ImageSharp.csproj | 5 + ...alizationSlidingWindowProcessor{TPixel}.cs | 2 +- .../ImageSharp.Benchmarks.csproj | 6 +- .../ImageSharp.Tests.ProfilingSandbox.csproj | 6 +- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 7 +- 8 files changed, 125 insertions(+), 49 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 4828f4f21..5189f0435 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -1,19 +1,37 @@ 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 @@ -52,37 +70,38 @@ jobs: codecov: false runs-on: ${{matrix.options.os}} - if: "!contains(github.event.head_commit.message, '[skip ci]')" steps: - - uses: actions/checkout@v2 + - 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: Create LFS file list + - name: Git Create LFS FileList run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id - - name: Restore LFS cache + - 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 LFS Pull + - name: Git Pull LFS run: git lfs pull - - name: Install NuGet + - name: NuGet Install uses: NuGet/setup-nuget@v1 - - name: Setup Git - shell: bash - run: | - git config --global core.autocrlf false - git config --global core.longpaths true - git fetch --prune --unshallow - git submodule -q update --init --recursive - - - name: Setup NuGet Cache + - name: NuGet Setup Cache uses: actions/cache@v2 id: nuget-cache with: @@ -90,60 +109,94 @@ jobs: key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }} restore-keys: ${{ runner.os }}-nuget- - - name: Build + - 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: Test + - 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 + 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: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip + path: tests/Images/ActualOutput/ - - name: Update Codecov + - name: Codecov Update uses: codecov/codecov-action@v1 if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors') with: - flags: unittests + flags: unittests Publish: needs: [Build] - runs-on: windows-latest + runs-on: ubuntu-latest if: (github.event_name == 'push') steps: - - uses: actions/checkout@v2 - - - name: Install NuGet - uses: NuGet/setup-nuget@v1 - - - name: Setup Git + - name: Git Config shell: bash run: | git config --global core.autocrlf false git config --global core.longpaths true - git fetch --prune --unshallow - git submodule -q update --init --recursive - - name: Pack + - 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: Publish to MyGet + - name: MyGet Publish shell: pwsh run: | - nuget.exe push .\artifacts\*.nupkg ${{secrets.MYGET_TOKEN}} -Source https://www.myget.org/F/sixlabors/api/v2/package - nuget.exe push .\artifacts\*.snupkg ${{secrets.MYGET_TOKEN}} -Source https://www.myget.org/F/sixlabors/api/v3/index.json + 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/Directory.Build.props b/Directory.Build.props index b3e18e5a5..3899ce939 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -18,6 +18,11 @@ + + + preview + + false Debug;Release;Debug-InnerLoop;Release-InnerLoop - 9 + + + net6.0;net5.0;netcoreapp3.1;netcoreapp2.1;net472 + + netcoreapp3.1 diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj index 10deb24c6..1a470fa31 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -14,10 +14,14 @@ false Debug;Release;Debug-InnerLoop;Release-InnerLoop false - 9 + + + net6.0;net5.0;netcoreapp3.1;netcoreapp2.1;net472 + + netcoreapp3.1 diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 30bd544fa..471287006 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -10,6 +10,11 @@ + + + net6.0;net5.0;netcoreapp3.1;netcoreapp2.1;net472 + + netcoreapp3.1 @@ -21,7 +26,7 @@ - +