diff --git a/.editorconfig b/.editorconfig
index 33fd0577a..753a4dbc2 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -122,7 +122,7 @@ dotnet_style_coalesce_expression = true:warning
dotnet_style_null_propagation = true:warning
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
# File header preferences
-file_header_template = Copyright (c) Six Labors.\nLicensed under the Apache License, Version 2.0.
+file_header_template = Copyright (c) Six Labors.\nLicensed under the Six Labors Split License.
# SA1636: File header copyright text should match
# Justification: .editorconfig supports file headers. If this is changed to a value other than "none", a stylecop.json file will need to added to the project.
# dotnet_diagnostic.SA1636.severity = none
diff --git a/.gitattributes b/.gitattributes
index 355b64dce..2fdea90e1 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -130,3 +130,4 @@
*.ppm filter=lfs diff=lfs merge=lfs -text
*.pnm filter=lfs diff=lfs merge=lfs -text
*.wbmp filter=lfs diff=lfs merge=lfs -text
+*.exr filter=lfs diff=lfs merge=lfs -text
diff --git a/.github/ISSUE_TEMPLATE/commercial-bug-report.yml b/.github/ISSUE_TEMPLATE/commercial-bug-report.yml
index e96677b24..6b4d914d7 100644
--- a/.github/ISSUE_TEMPLATE/commercial-bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/commercial-bug-report.yml
@@ -4,6 +4,20 @@ description: |
Please contact help@sixlabors.com for issues requiring private support.
labels: ["commercial", "needs triage"]
body:
+- type: checkboxes
+ attributes:
+ label: Prerequisites
+ options:
+ - label: I have bought a Commercial License
+ required: true
+ - label: I have written a descriptive issue title
+ required: true
+ - label: I have verified that I am running the latest version of ImageSharp
+ required: true
+ - label: I have verified if the problem exist in both `DEBUG` and `RELEASE` mode
+ required: true
+ - label: I have searched [open](https://github.com/SixLabors/ImageSharp/issues) and [closed](https://github.com/SixLabors/ImageSharp/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported
+ required: true
- type: input
attributes:
label: ImageSharp version
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 62a8bf2b4..729f66a6b 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,8 @@
blank_issues_enabled: false
contact_links:
+ - name: Questions
+ url: https://github.com/SixLabors/ImageSharp/discussions/categories/q-a
+ about: Ask the community for help.
- name: Feature Request
- url: https://github.com/SixLabors/ImageSharp/discussions?discussions_q=category%3AIdeas
+ url: https://github.com/SixLabors/ImageSharp/discussions/categories/ideas
about: Share ideas for new features for this project.
diff --git a/.github/ISSUE_TEMPLATE/oss-bug-report.yml b/.github/ISSUE_TEMPLATE/oss-bug-report.yml
index 00138d511..a4e5619d4 100644
--- a/.github/ISSUE_TEMPLATE/oss-bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/oss-bug-report.yml
@@ -2,6 +2,18 @@ name: "OSS : Bug Report"
description: Create a report to help us improve the project. OSS Issues are not guaranteed to be triaged.
labels: ["needs triage"]
body:
+- type: checkboxes
+ attributes:
+ label: Prerequisites
+ options:
+ - label: I have written a descriptive issue title
+ required: true
+ - label: I have verified that I am running the latest version of ImageSharp
+ required: true
+ - label: I have verified if the problem exist in both `DEBUG` and `RELEASE` mode
+ required: true
+ - label: I have searched [open](https://github.com/SixLabors/ImageSharp/issues) and [closed](https://github.com/SixLabors/ImageSharp/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported
+ required: true
- type: input
attributes:
label: ImageSharp version
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..5ace4600a
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 43a4a1708..1a57ec1ed 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -15,59 +15,39 @@ jobs:
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
+ framework: net7.0
+ sdk: 7.0.x
sdk-preview: true
runtime: -x64
codecov: false
+ # Temp disabled due to runtime preview issues.
+ #- os: macos-latest
+ # framework: net7.0
+ # sdk: 7.0.x
+ # sdk-preview: true
+ # runtime: -x64
+ # codecov: false
- os: windows-latest
- framework: net6.0
- sdk: 6.0.x
+ framework: net7.0
+ sdk: 7.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
+ framework: net6.0
+ sdk: 6.0.x
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
+ framework: net6.0
+ sdk: 6.0.x
runtime: -x64
codecov: false
- os: windows-latest
- framework: net472
+ framework: net6.0
+ sdk: 6.0.x
runtime: -x64
codecov: false
- - os: windows-latest
- framework: net472
- runtime: -x86
- codecov: false
runs-on: ${{matrix.options.os}}
@@ -79,7 +59,7 @@ jobs:
git config --global core.longpaths true
- name: Git Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
@@ -89,7 +69,7 @@ jobs:
run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id
- name: Git Setup LFS Cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
id: lfs-cache
with:
path: .git/lfs
@@ -102,7 +82,7 @@ jobs:
uses: NuGet/setup-nuget@v1
- name: NuGet Setup Cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
id: nuget-cache
with:
path: ~/.nuget
@@ -110,13 +90,20 @@ jobs:
restore-keys: ${{ runner.os }}-nuget-
- name: DotNet Setup
- uses: actions/setup-dotnet@v1
+ if: ${{ matrix.options.sdk-preview != true }}
+ uses: actions/setup-dotnet@v2
with:
+ include-prerelease: true
dotnet-version: |
6.0.x
- 5.0.x
- 3.1.x
- 2.1.x
+
+ - name: DotNet Setup Preview
+ if: ${{ matrix.options.sdk-preview == true }}
+ uses: actions/setup-dotnet@v2
+ with:
+ include-prerelease: true
+ dotnet-version: |
+ 7.0.x
- name: DotNet Build
if: ${{ matrix.options.sdk-preview != true }}
@@ -149,7 +136,7 @@ jobs:
XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
- name: Export Failed Output
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
if: failure()
with:
name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip
@@ -170,7 +157,7 @@ jobs:
git config --global core.longpaths true
- name: Git Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
@@ -179,7 +166,7 @@ jobs:
uses: NuGet/setup-nuget@v1
- name: NuGet Setup Cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
id: nuget-cache
with:
path: ~/.nuget
diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
index 2b14f2a4b..85ff42b74 100644
--- a/.github/workflows/code-coverage.yml
+++ b/.github/workflows/code-coverage.yml
@@ -10,7 +10,7 @@ jobs:
matrix:
options:
- os: ubuntu-latest
- framework: netcoreapp3.1
+ framework: net6.0
runtime: -x64
codecov: true
@@ -24,7 +24,7 @@ jobs:
git config --global core.longpaths true
- name: Git Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
@@ -34,7 +34,7 @@ jobs:
run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id
- name: Git Setup LFS Cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
id: lfs-cache
with:
path: .git/lfs
@@ -47,13 +47,19 @@ jobs:
uses: NuGet/setup-nuget@v1
- name: NuGet Setup Cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
id: nuget-cache
with:
path: ~/.nuget
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
restore-keys: ${{ runner.os }}-nuget-
+ - name: DotNet Setup
+ uses: actions/setup-dotnet@v2
+ with:
+ dotnet-version: |
+ 6.0.x
+
- name: DotNet Build
shell: pwsh
run: ./ci-build.ps1 "${{matrix.options.framework}}"
@@ -68,14 +74,14 @@ jobs:
XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
- name: Export Failed Output
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
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
+ uses: codecov/codecov-action@v3
if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors')
with:
flags: unittests
diff --git a/ImageSharp.sln b/ImageSharp.sln
index 17d293b43..3ea3160a7 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -28,9 +28,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{1799
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEMPLATE", "{FBE8C1AD-5AEC-4514-9B64-091D8E145865}"
ProjectSection(SolutionItems) = preProject
- .github\ISSUE_TEMPLATE\commercial-bug-report.md = .github\ISSUE_TEMPLATE\commercial-bug-report.md
+ .github\ISSUE_TEMPLATE\commercial-bug-report.yml = .github\ISSUE_TEMPLATE\commercial-bug-report.yml
.github\ISSUE_TEMPLATE\config.yml = .github\ISSUE_TEMPLATE\config.yml
- .github\ISSUE_TEMPLATE\oss-bug-report.md = .github\ISSUE_TEMPLATE\oss-bug-report.md
+ .github\ISSUE_TEMPLATE\oss-bug-report.yml = .github\ISSUE_TEMPLATE\oss-bug-report.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}"
@@ -142,6 +142,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gif", "Gif", "{EE3FB0B3-1C3
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{BF8DFDC1-CEE5-4A37-B216-D3085360C776}"
ProjectSection(SolutionItems) = preProject
+ tests\Images\Input\Gif\issues\bugzilla-55918.gif = tests\Images\Input\Gif\issues\bugzilla-55918.gif
+ tests\Images\Input\Gif\issues\issue1505_argumentoutofrange.png = tests\Images\Input\Gif\issues\issue1505_argumentoutofrange.png
+ tests\Images\Input\Gif\issues\issue1530.gif = tests\Images\Input\Gif\issues\issue1530.gif
+ tests\Images\Input\Gif\issues\issue1668_invalidcolorindex.gif = tests\Images\Input\Gif\issues\issue1668_invalidcolorindex.gif
+ tests\Images\Input\Gif\issues\issue1962_tiniest_gif_1st.gif = tests\Images\Input\Gif\issues\issue1962_tiniest_gif_1st.gif
+ tests\Images\Input\Gif\issues\issue2012_drona1.gif = tests\Images\Input\Gif\issues\issue2012_drona1.gif
+ tests\Images\Input\Gif\issues\issue2012_Stronghold-Crusader-Extreme-Cover.gif = tests\Images\Input\Gif\issues\issue2012_Stronghold-Crusader-Extreme-Cover.gif
tests\Images\Input\Gif\issues\issue403_baddescriptorwidth.gif = tests\Images\Input\Gif\issues\issue403_baddescriptorwidth.gif
tests\Images\Input\Gif\issues\issue405_badappextlength252-2.gif = tests\Images\Input\Gif\issues\issue405_badappextlength252-2.gif
tests\Images\Input\Gif\issues\issue405_badappextlength252.gif = tests\Images\Input\Gif\issues\issue405_badappextlength252.gif
@@ -641,49 +648,27 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tga", "Tga", "{5DFC394F-136
EndProjectSection
EndProject
Global
- GlobalSection(SharedMSBuildProjectFiles) = preSolution
- shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{2aa31a1f-142c-43f4-8687-09abca4b3a26}*SharedItemsImports = 5
- shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13
- EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
- Debug-InnerLoop|Any CPU = Debug-InnerLoop|Any CPU
Release|Any CPU = Release|Any CPU
- Release-InnerLoop|Any CPU = Release-InnerLoop|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU
- {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|Any CPU.Build.0 = Release|Any CPU
- {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU
- {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU
- {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|Any CPU.Build.0 = Release|Any CPU
- {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU
- {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU
- {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|Any CPU.Build.0 = Release|Any CPU
- {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU
- {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU
{FC527290-2F22-432C-B77B-6E815726B02C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC527290-2F22-432C-B77B-6E815726B02C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU
- {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU
{FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.Build.0 = Release|Any CPU
- {FC527290-2F22-432C-B77B-6E815726B02C}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU
- {FC527290-2F22-432C-B77B-6E815726B02C}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -718,6 +703,10 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795}
EndGlobalSection
+ GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{2aa31a1f-142c-43f4-8687-09abca4b3a26}*SharedItemsImports = 5
+ shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13
+ EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
diff --git a/LICENSE b/LICENSE
index 8d5852d37..a68eb6783 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,201 +1,43 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
+Six Labors Split License
+Version 1.0, June 2022
+Copyright (c) Six Labors
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
- 1. Definitions.
+1. Definitions.
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
+ "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
+ "Source" form shall mean the preferred form for making modifications, including but not limited to software source
+ code, documentation source, and configuration files.
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
+ "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including
+ but not limited to compiled object code, generated documentation, and conversions to other media types.
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
+ "Work" (or "Works") shall mean any Six Labors software made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work.
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
+ "Direct Package Dependency" shall mean any Work in Source or Object form that is installed directly by You.
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
+ "Transitive Package Dependency" shall mean any Work in Object form that is installed indirectly by a third party
+ dependency unrelated to Six Labors.
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
+2. License
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
+ Works in Source or Object form are split licensed and may be licensed under the Apache License, Version 2.0 or a
+ Six Labors Commercial Use License.
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
+ Licenses are granted based upon You meeting the qualified criteria as stated. Once granted,
+ You must reference the granted license only in all documentation.
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
+ Works in Source or Object form are licensed to You under the Apache License, Version 2.0 if.
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
+ - You are consuming the Work in for use in software licensed under an Open Source or Source Available license.
+ - You are consuming the Work as a Transitive Package Dependency.
+ - You are consuming the Work as a Direct Package Dependency in the capacity of a For-profit company/individual with
+ less than 1M USD annual gross revenue.
+ - You are consuming the Work as a Direct Package Dependency in the capacity of a Non-profit organization
+ or Registered Charity.
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright (c) Six Labors
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ For all other scenarios, Works in Source or Object form are licensed to You under the Six Labors Commercial License
+ which may be purchased by visiting https://sixlabors.com/pricing/.
diff --git a/README.md b/README.md
index 2492041db..34466eccd 100644
--- a/README.md
+++ b/README.md
@@ -9,30 +9,29 @@ SixLabors.ImageSharp
[](https://github.com/SixLabors/ImageSharp/actions)
[](https://codecov.io/gh/SixLabors/ImageSharp)
-[](https://opensource.org/licenses/Apache-2.0)
+[](https://github.com/SixLabors/ImageSharp/blob/main/LICENSE)
[](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fSixLabors%2fImageSharp&via=sixlabors)
### **ImageSharp** is a new, fully featured, fully managed, cross-platform, 2D graphics API.
-ImageSharp is a new, fully featured, fully managed, cross-platform, 2D graphics library. Designed to simplify image processing, ImageSharp brings you an incredibly powerful yet beautifully simple API.
+ImageSharp is a new, fully featured, fully managed, cross-platform, 2D graphics library.
+Designed to simplify image processing, ImageSharp brings you an incredibly powerful yet beautifully simple API.
ImageSharp is designed from the ground up to be flexible and extensible. The library provides API endpoints for common image processing operations and the building blocks to allow for the development of additional operations.
-Built against [.NET Standard 2.0](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), ImageSharp can be used in device, cloud, and embedded/IoT scenarios.
+Built against [.NET 6](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), ImageSharp can be used in device, cloud, and embedded/IoT scenarios.
## License
-- ImageSharp is licensed under the [Apache License, Version 2.0](https://opensource.org/licenses/Apache-2.0)
-- An alternative Commercial Support License can be purchased **for projects and applications requiring support**.
-Please visit https://sixlabors.com/pricing for details.
+- ImageSharp is licensed under the [Six Labors Split License, Version 1.0](https://github.com/SixLabors/ImageSharp/blob/main/LICENSE)
## Support Six Labors
Support the efforts of the development of the Six Labors projects.
- - [Purchase a Commercial Support License :heart:](https://sixlabors.com/pricing/)
+ - [Purchase a Commercial License :heart:](https://sixlabors.com/pricing/)
- [Become a sponsor via GitHub Sponsors :heart:]( https://github.com/sponsors/SixLabors)
- [Become a sponsor via Open Collective :heart:](https://opencollective.com/sixlabors)
@@ -43,7 +42,8 @@ Support the efforts of the development of the Six Labors projects.
## Questions
-- Do you have questions? We are happy to help! Please [join our Discussions Forum](https://github.com/SixLabors/ImageSharp/discussions/category_choices), or ask them on [Stack Overflow](https://stackoverflow.com) using the `ImageSharp` tag. Please do not open issues for questions.
+- Do you have questions? Please [join our Discussions Forum](https://github.com/SixLabors/ImageSharp/discussions/categories/q-a). Do not open issues for questions.
+- For feature ideas please [join our Discussions Forum](https://github.com/SixLabors/ImageSharp/discussions/categories/ideas) and we'll be happy to discuss.
- Please read our [Contribution Guide](https://github.com/SixLabors/ImageSharp/blob/main/.github/CONTRIBUTING.md) before opening issues or pull requests!
## Code of Conduct
@@ -62,9 +62,9 @@ Install stable releases via Nuget; development releases are available via MyGet.
If you prefer, you can compile ImageSharp yourself (please do and help!)
-- Using [Visual Studio 2019](https://visualstudio.microsoft.com/vs/)
+- Using [Visual Studio 2022](https://visualstudio.microsoft.com/vs/)
- Make sure you have the latest version installed
- - Make sure you have [the .NET 5 SDK](https://www.microsoft.com/net/core#windows) installed
+ - Make sure you have [the .NET 6 SDK](https://www.microsoft.com/net/core#windows) installed
Alternatively, you can work from command line and/or with a lightweight editor on **both Linux/Unix and Windows**:
diff --git a/shared-infrastructure b/shared-infrastructure
index 59ce17f5a..c0e0353c1 160000
--- a/shared-infrastructure
+++ b/shared-infrastructure
@@ -1 +1 @@
-Subproject commit 59ce17f5a4e1f956811133f41add7638e74c2836
+Subproject commit c0e0353c1ee89398def0ccdc3e945380034fbea8
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index d211992a9..faa29865f 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -27,6 +27,7 @@
+
diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
index 829c6155d..8a7046c9a 100644
--- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
+++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Collections.Generic;
diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs
index 82a146dc7..7db2b7f96 100644
--- a/src/ImageSharp/Advanced/AotCompilerTools.cs
+++ b/src/ImageSharp/Advanced/AotCompilerTools.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Diagnostics.CodeAnalysis;
@@ -287,8 +287,7 @@ namespace SixLabors.ImageSharp.Advanced
where TPixel : unmanaged, IPixel
where TDecoder : class, IImageDecoder
{
- default(TDecoder).Decode(default, default);
- default(TDecoder).DecodeAsync(default, default, default);
+ default(TDecoder).Decode(default, default, default);
}
///
diff --git a/src/ImageSharp/Advanced/IConfigurationProvider.cs b/src/ImageSharp/Advanced/IConfigurationProvider.cs
index 9c9d2a942..da1f6c1fd 100644
--- a/src/ImageSharp/Advanced/IConfigurationProvider.cs
+++ b/src/ImageSharp/Advanced/IConfigurationProvider.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Advanced
{
diff --git a/src/ImageSharp/Advanced/IImageVisitor.cs b/src/ImageSharp/Advanced/IImageVisitor.cs
index ccff18026..2f2aa7a31 100644
--- a/src/ImageSharp/Advanced/IImageVisitor.cs
+++ b/src/ImageSharp/Advanced/IImageVisitor.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Threading;
using System.Threading.Tasks;
diff --git a/src/ImageSharp/Advanced/IPixelSource.cs b/src/ImageSharp/Advanced/IPixelSource.cs
index 948abe0be..d6d541536 100644
--- a/src/ImageSharp/Advanced/IPixelSource.cs
+++ b/src/ImageSharp/Advanced/IPixelSource.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
diff --git a/src/ImageSharp/Advanced/IRowIntervalOperation.cs b/src/ImageSharp/Advanced/IRowIntervalOperation.cs
index cc7072ff9..33a9bc40c 100644
--- a/src/ImageSharp/Advanced/IRowIntervalOperation.cs
+++ b/src/ImageSharp/Advanced/IRowIntervalOperation.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Memory;
diff --git a/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs b/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs
index a76624e1a..b1e6dae31 100644
--- a/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs
+++ b/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using SixLabors.ImageSharp.Memory;
diff --git a/src/ImageSharp/Advanced/IRowOperation.cs b/src/ImageSharp/Advanced/IRowOperation.cs
index 122296172..8a7d62e0f 100644
--- a/src/ImageSharp/Advanced/IRowOperation.cs
+++ b/src/ImageSharp/Advanced/IRowOperation.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Advanced
{
diff --git a/src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs b/src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs
index 5e1562a79..cf491c82f 100644
--- a/src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs
+++ b/src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Advanced/ParallelExecutionSettings.cs b/src/ImageSharp/Advanced/ParallelExecutionSettings.cs
index e1f36d9d6..01b1a1538 100644
--- a/src/ImageSharp/Advanced/ParallelExecutionSettings.cs
+++ b/src/ImageSharp/Advanced/ParallelExecutionSettings.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Threading.Tasks;
diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs b/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs
index 3c2b9fd2c..d2d01ebdf 100644
--- a/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs
+++ b/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs
index e787b7cfc..d37a76951 100644
--- a/src/ImageSharp/Advanced/ParallelRowIterator.cs
+++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
diff --git a/src/ImageSharp/Advanced/PreserveAttribute.cs b/src/ImageSharp/Advanced/PreserveAttribute.cs
index a16b30e23..d543a043c 100644
--- a/src/ImageSharp/Advanced/PreserveAttribute.cs
+++ b/src/ImageSharp/Advanced/PreserveAttribute.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Advanced
{
diff --git a/src/ImageSharp/Color/Color.Conversions.cs b/src/ImageSharp/Color/Color.Conversions.cs
index 5c10bfaa0..2e8f4100e 100644
--- a/src/ImageSharp/Color/Color.Conversions.cs
+++ b/src/ImageSharp/Color/Color.Conversions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Color/Color.NamedColors.cs b/src/ImageSharp/Color/Color.NamedColors.cs
index 609191d5e..8a01b9e6e 100644
--- a/src/ImageSharp/Color/Color.NamedColors.cs
+++ b/src/ImageSharp/Color/Color.NamedColors.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Collections.Generic;
diff --git a/src/ImageSharp/Color/Color.WebSafePalette.cs b/src/ImageSharp/Color/Color.WebSafePalette.cs
index 1cffb841c..40275bad4 100644
--- a/src/ImageSharp/Color/Color.WebSafePalette.cs
+++ b/src/ImageSharp/Color/Color.WebSafePalette.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Color/Color.WernerPalette.cs b/src/ImageSharp/Color/Color.WernerPalette.cs
index 52299ae8f..6083112db 100644
--- a/src/ImageSharp/Color/Color.WernerPalette.cs
+++ b/src/ImageSharp/Color/Color.WernerPalette.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs
index cd0583361..d8fb34891 100644
--- a/src/ImageSharp/Color/Color.cs
+++ b/src/ImageSharp/Color/Color.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs
index c1b9aab37..b72a2eecd 100644
--- a/src/ImageSharp/ColorSpaces/CieLab.cs
+++ b/src/ImageSharp/ColorSpaces/CieLab.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs
index 7722b705e..899c7d8db 100644
--- a/src/ImageSharp/ColorSpaces/CieLch.cs
+++ b/src/ImageSharp/ColorSpaces/CieLch.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs
index ed8e72fc9..2a41fd490 100644
--- a/src/ImageSharp/ColorSpaces/CieLchuv.cs
+++ b/src/ImageSharp/ColorSpaces/CieLchuv.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs
index 6b69b9088..a45042ba8 100644
--- a/src/ImageSharp/ColorSpaces/CieLuv.cs
+++ b/src/ImageSharp/ColorSpaces/CieLuv.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs
index 5e3b444ac..9306606db 100644
--- a/src/ImageSharp/ColorSpaces/CieXyy.cs
+++ b/src/ImageSharp/ColorSpaces/CieXyy.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs
index ceffd727d..e52904c55 100644
--- a/src/ImageSharp/ColorSpaces/CieXyz.cs
+++ b/src/ImageSharp/ColorSpaces/CieXyz.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs
index fb8efad63..cd2899ee7 100644
--- a/src/ImageSharp/ColorSpaces/Cmyk.cs
+++ b/src/ImageSharp/ColorSpaces/Cmyk.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs
index 440aa4185..db2c21448 100644
--- a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
index 5cd89abfd..211005ac8 100644
--- a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs
index 957c07687..372dc7ac2 100644
--- a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs
index 8b511aa1c..f753d16dc 100644
--- a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
index dc6c960aa..1337af702 100644
--- a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs b/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs
index 0d3568a2a..62fa445c5 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
index f39d5049c..60ae18b5c 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
index 86075e002..1a7c627db 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
index da2e80844..06ff11b1e 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
index 7f100428b..5752cb9b1 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
index 1c831f714..486924b9a 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
index 0adac2201..603308751 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
index b069bb72c..242392acc 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
index 5fa5ec8b1..664511be9 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
index f88076706..666a6b03c 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
index 6d784575c..615a26e36 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
index 147ffba70..ad2fbd626 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
index 7156ac82f..c317c8e8b 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
index 7f44a3e4b..be36c46dc 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
index ce09b1148..cfb6d3b76 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
index 126f1eb21..db6dc2133 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs
index f565e6aa6..001719046 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs
index e91c83624..ceb7f24ad 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs
index 36dd2445e..728d7cd22 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs
index 0b6ca4071..6485da076 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs
index 25542f559..0aefeda85 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
index 34354efe5..3634ebe99 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs
index 052db0e77..fc064a6de 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs
index 13644b092..d69fb4697 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
index 12c65105f..0b4a4a9b8 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs
index ea021d73c..5db91eb75 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs
index 7ed2d78d8..5bb394dfa 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs
index 22f081ccd..533d94465 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs
index 5f16a82a4..641ee8077 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs
index 031d96e71..48843dd81 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
index 0b70f8c85..494c7b3c8 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs
index f6ee2b0d8..c9ae11ffc 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs
index 72f543442..20a03fb41 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs
index d0e0da756..c5a599e9e 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs
index f005e025a..de187ee59 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
index f120d6f3d..a804d1d5f 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs
index 556334b4d..8a406cb33 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs
index c52a91e6f..f124fdd80 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs
index 7e9e3210a..ea5ef2bc1 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs
index 056f89608..5ce09802c 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs
index 3f90e8d71..727c89dd7 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs
index b787c48b3..703b3a748 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs
index f69868760..ea0dfbb4c 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
index 2a03b54e7..a38626020 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs
index 6dec4d735..b107aacaf 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
index f9b268307..9c2bc6f02 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs
index 7d42759ae..f608baac4 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs
index d57936e35..b675fe53d 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs
index 5d556fa0d..6df9a17b9 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs
index 996b72094..ad869a75b 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs
index 8140d24db..236da1fd0 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs
index 740752e6d..98f9bdb7c 100644
--- a/src/ImageSharp/ColorSpaces/Hsl.cs
+++ b/src/ImageSharp/ColorSpaces/Hsl.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs
index d29e4b5b7..a44aebbb1 100644
--- a/src/ImageSharp/ColorSpaces/Hsv.cs
+++ b/src/ImageSharp/ColorSpaces/Hsv.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs
index a36ad4b9e..c3d808c6c 100644
--- a/src/ImageSharp/ColorSpaces/HunterLab.cs
+++ b/src/ImageSharp/ColorSpaces/HunterLab.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Illuminants.cs b/src/ImageSharp/ColorSpaces/Illuminants.cs
index f22ab9cd0..4f14982fa 100644
--- a/src/ImageSharp/ColorSpaces/Illuminants.cs
+++ b/src/ImageSharp/ColorSpaces/Illuminants.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.ColorSpaces
{
diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs
index 245dbbd0f..5dffea678 100644
--- a/src/ImageSharp/ColorSpaces/LinearRgb.cs
+++ b/src/ImageSharp/ColorSpaces/LinearRgb.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs
index e0068c92f..7ca8c3cf0 100644
--- a/src/ImageSharp/ColorSpaces/Lms.cs
+++ b/src/ImageSharp/ColorSpaces/Lms.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs
index 900f71b2c..4902d98fd 100644
--- a/src/ImageSharp/ColorSpaces/Rgb.cs
+++ b/src/ImageSharp/ColorSpaces/Rgb.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs
index 07f76e58c..00cca02da 100644
--- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs
+++ b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.ColorSpaces.Companding;
using SixLabors.ImageSharp.ColorSpaces.Conversion;
diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs
index b39fe3025..cb4d7d091 100644
--- a/src/ImageSharp/ColorSpaces/YCbCr.cs
+++ b/src/ImageSharp/ColorSpaces/YCbCr.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/Common/ByteOrder.cs b/src/ImageSharp/Common/ByteOrder.cs
index cc38f1cde..603384b3a 100644
--- a/src/ImageSharp/Common/ByteOrder.cs
+++ b/src/ImageSharp/Common/ByteOrder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp
{
diff --git a/src/ImageSharp/Common/Constants.cs b/src/ImageSharp/Common/Constants.cs
index 90f33fdf7..0a3378939 100644
--- a/src/ImageSharp/Common/Constants.cs
+++ b/src/ImageSharp/Common/Constants.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp
{
diff --git a/src/ImageSharp/Common/Exceptions/ImageFormatException.cs b/src/ImageSharp/Common/Exceptions/ImageFormatException.cs
index 6a54ce548..0c50410a5 100644
--- a/src/ImageSharp/Common/Exceptions/ImageFormatException.cs
+++ b/src/ImageSharp/Common/Exceptions/ImageFormatException.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs b/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs
index dc4f4f108..51c238af8 100644
--- a/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs
+++ b/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Common/Exceptions/InvalidImageContentException.cs b/src/ImageSharp/Common/Exceptions/InvalidImageContentException.cs
index e4713e237..7b48c9fe9 100644
--- a/src/ImageSharp/Common/Exceptions/InvalidImageContentException.cs
+++ b/src/ImageSharp/Common/Exceptions/InvalidImageContentException.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using SixLabors.ImageSharp.Memory;
diff --git a/src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs b/src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs
index f3a1ea0f5..beab6586b 100644
--- a/src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs
+++ b/src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp
{
diff --git a/src/ImageSharp/Common/Extensions/ConfigurationExtensions.cs b/src/ImageSharp/Common/Extensions/ConfigurationExtensions.cs
index 9bf0a1fbe..883468abd 100644
--- a/src/ImageSharp/Common/Extensions/ConfigurationExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/ConfigurationExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Threading.Tasks;
diff --git a/src/ImageSharp/Common/Extensions/EncoderExtensions.cs b/src/ImageSharp/Common/Extensions/EncoderExtensions.cs
index caef4ac92..b49e1234f 100644
--- a/src/ImageSharp/Common/Extensions/EncoderExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/EncoderExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if !SUPPORTS_ENCODING_STRING
using System;
diff --git a/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs b/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs
index c6560f8c3..92fba789a 100644
--- a/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Collections.Generic;
diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
index 1193eccee..e21d9e96b 100644
--- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
@@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp
internal static class StreamExtensions
{
///
- /// Writes data from a stream into the provided buffer.
+ /// Writes data from a stream from the provided buffer.
///
/// The stream.
/// The buffer.
diff --git a/src/ImageSharp/Common/Helpers/ColorNumerics.cs b/src/ImageSharp/Common/Helpers/ColorNumerics.cs
index 6f225b110..8ae344a4e 100644
--- a/src/ImageSharp/Common/Helpers/ColorNumerics.cs
+++ b/src/ImageSharp/Common/Helpers/ColorNumerics.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs
index f438ca9e2..6e1ed2181 100644
--- a/src/ImageSharp/Common/Helpers/DebugGuard.cs
+++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Diagnostics;
diff --git a/src/ImageSharp/Common/Helpers/EnumUtils.cs b/src/ImageSharp/Common/Helpers/EnumUtils.cs
index 089aba337..a59841162 100644
--- a/src/ImageSharp/Common/Helpers/EnumUtils.cs
+++ b/src/ImageSharp/Common/Helpers/EnumUtils.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
@@ -19,15 +19,14 @@ namespace SixLabors.ImageSharp
/// The default value to return.
/// The .
public static TEnum Parse(int value, TEnum defaultValue)
- where TEnum : Enum
+ where TEnum : struct, Enum
{
- foreach (TEnum enumValue in Enum.GetValues(typeof(TEnum)))
+ DebugGuard.IsTrue(Unsafe.SizeOf() == sizeof(int), "Only int-sized enums are supported.");
+
+ TEnum valueEnum = Unsafe.As(ref value);
+ if (Enum.IsDefined(valueEnum))
{
- TEnum current = enumValue;
- if (value == Unsafe.As(ref current))
- {
- return enumValue;
- }
+ return valueEnum;
}
return defaultValue;
@@ -41,8 +40,10 @@ namespace SixLabors.ImageSharp
/// The flag.
/// The .
public static bool HasFlag(TEnum value, TEnum flag)
- where TEnum : Enum
+ where TEnum : struct, Enum
{
+ DebugGuard.IsTrue(Unsafe.SizeOf() == sizeof(int), "Only int-sized enums are supported.");
+
uint flagValue = Unsafe.As(ref flag);
return (Unsafe.As(ref value) & flagValue) == flagValue;
}
diff --git a/src/ImageSharp/Common/Helpers/ExifResolutionValues.cs b/src/ImageSharp/Common/Helpers/ExifResolutionValues.cs
index b6a628608..704f75d4f 100644
--- a/src/ImageSharp/Common/Helpers/ExifResolutionValues.cs
+++ b/src/ImageSharp/Common/Helpers/ExifResolutionValues.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Common.Helpers
{
diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs
index 0f6efcb3c..fb18392f3 100644
--- a/src/ImageSharp/Common/Helpers/Guard.cs
+++ b/src/ImageSharp/Common/Helpers/Guard.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Common/Helpers/HexConverter.cs b/src/ImageSharp/Common/Helpers/HexConverter.cs
index c55e9bbd9..27a5a40f6 100644
--- a/src/ImageSharp/Common/Helpers/HexConverter.cs
+++ b/src/ImageSharp/Common/Helpers/HexConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Common/Helpers/InliningOptions.cs b/src/ImageSharp/Common/Helpers/InliningOptions.cs
index 1ae880787..a6c6d021c 100644
--- a/src/ImageSharp/Common/Helpers/InliningOptions.cs
+++ b/src/ImageSharp/Common/Helpers/InliningOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
// Uncomment this for verbose profiler results. DO NOT PUSH TO MAIN!
// #define PROFILING
diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs
index 7de838bc9..c149cc7b6 100644
--- a/src/ImageSharp/Common/Helpers/Numerics.cs
+++ b/src/ImageSharp/Common/Helpers/Numerics.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
@@ -75,6 +75,12 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Modulo8(int x) => x & 7;
+ ///
+ /// Calculates % 8
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static nint Modulo8(nint x) => x & 7;
+
///
/// Fast (x mod m) calculator, with the restriction that
/// should be power of 2.
@@ -968,7 +974,7 @@ namespace SixLabors.ImageSharp
/// Tells whether input value is outside of the given range.
///
/// Value.
- /// Mininum value, inclusive.
+ /// Minimum value, inclusive.
/// Maximum value, inclusive.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsOutOfRange(int value, int min, int max)
diff --git a/src/ImageSharp/Common/Helpers/RuntimeEnvironment.cs b/src/ImageSharp/Common/Helpers/RuntimeEnvironment.cs
index 5525d3de5..925925ff9 100644
--- a/src/ImageSharp/Common/Helpers/RuntimeEnvironment.cs
+++ b/src/ImageSharp/Common/Helpers/RuntimeEnvironment.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.InteropServices;
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs
index 049c61185..0d27ea437 100644
--- a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs
+++ b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs
index 0c2b1d508..cb85a550d 100644
--- a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs
+++ b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs
index 61e99890e..d5c8e17ec 100644
--- a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs
+++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs
index 3ecad3c5d..d50aab005 100644
--- a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs
+++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
index 75555f88a..7e878677f 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
index 0abc0e26d..336ff3abc 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs
index 15133770f..c035ac72f 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
index cd96b51e9..01b68f9fa 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System;
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs
index 1ccf5ab1a..3ee2086cf 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs
index abf9e9fed..db86afd64 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Diagnostics;
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs
index 29068a82c..0384cc4ed 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Diagnostics;
diff --git a/src/ImageSharp/Common/Helpers/TestHelpers.cs b/src/ImageSharp/Common/Helpers/TestHelpers.cs
index 33aa81f3d..159fd95d3 100644
--- a/src/ImageSharp/Common/Helpers/TestHelpers.cs
+++ b/src/ImageSharp/Common/Helpers/TestHelpers.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Common.Helpers
{
diff --git a/src/ImageSharp/Common/Helpers/TolerantMath.cs b/src/ImageSharp/Common/Helpers/TolerantMath.cs
index d1d3f2174..f244a9169 100644
--- a/src/ImageSharp/Common/Helpers/TolerantMath.cs
+++ b/src/ImageSharp/Common/Helpers/TolerantMath.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs
index 7ea64aa62..ba7bdb0e0 100644
--- a/src/ImageSharp/Common/Helpers/UnitConverter.cs
+++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Metadata;
diff --git a/src/ImageSharp/Common/Tuples/Octet{T}.cs b/src/ImageSharp/Common/Tuples/Octet{T}.cs
index aaecafd09..08fa63943 100644
--- a/src/ImageSharp/Common/Tuples/Octet{T}.cs
+++ b/src/ImageSharp/Common/Tuples/Octet{T}.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
diff --git a/src/ImageSharp/Compression/Zlib/Adler32.cs b/src/ImageSharp/Compression/Zlib/Adler32.cs
index 7eb3f4516..7c3b4ae18 100644
--- a/src/ImageSharp/Compression/Zlib/Adler32.cs
+++ b/src/ImageSharp/Compression/Zlib/Adler32.cs
@@ -1,8 +1,9 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
@@ -31,6 +32,8 @@ namespace SixLabors.ImageSharp.Compression.Zlib
#if SUPPORTS_RUNTIME_INTRINSICS
private const int MinBufferSize = 64;
+ private const int BlockSize = 1 << 5;
+
// The C# compiler emits this as a compile-time constant embedded in the PE file.
private static ReadOnlySpan Tap1Tap2 => new byte[]
{
@@ -63,6 +66,11 @@ namespace SixLabors.ImageSharp.Compression.Zlib
}
#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Avx2.IsSupported && buffer.Length >= MinBufferSize)
+ {
+ return CalculateAvx2(adler, buffer);
+ }
+
if (Ssse3.IsSupported && buffer.Length >= MinBufferSize)
{
return CalculateSse(adler, buffer);
@@ -83,19 +91,15 @@ namespace SixLabors.ImageSharp.Compression.Zlib
uint s2 = (adler >> 16) & 0xFFFF;
// Process the data in blocks.
- const int BLOCK_SIZE = 1 << 5;
-
uint length = (uint)buffer.Length;
- uint blocks = length / BLOCK_SIZE;
- length -= blocks * BLOCK_SIZE;
+ uint blocks = length / BlockSize;
+ length -= blocks * BlockSize;
- int index = 0;
- fixed (byte* bufferPtr = buffer)
+ fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
- fixed (byte* tapPtr = Tap1Tap2)
+ fixed (byte* tapPtr = &MemoryMarshal.GetReference(Tap1Tap2))
{
- index += (int)blocks * BLOCK_SIZE;
- var localBufferPtr = bufferPtr;
+ byte* localBufferPtr = bufferPtr;
// _mm_setr_epi8 on x86
Vector128 tap1 = Sse2.LoadVector128((sbyte*)tapPtr);
@@ -105,7 +109,7 @@ namespace SixLabors.ImageSharp.Compression.Zlib
while (blocks > 0)
{
- uint n = NMAX / BLOCK_SIZE; /* The NMAX constraint. */
+ uint n = NMAX / BlockSize; /* The NMAX constraint. */
if (n > blocks)
{
n = blocks;
@@ -138,7 +142,7 @@ namespace SixLabors.ImageSharp.Compression.Zlib
Vector128 mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2);
v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones).AsUInt32());
- localBufferPtr += BLOCK_SIZE;
+ localBufferPtr += BlockSize;
}
while (--n > 0);
@@ -164,45 +168,127 @@ namespace SixLabors.ImageSharp.Compression.Zlib
if (length > 0)
{
- if (length >= 16)
- {
- s2 += s1 += localBufferPtr[0];
- s2 += s1 += localBufferPtr[1];
- s2 += s1 += localBufferPtr[2];
- s2 += s1 += localBufferPtr[3];
- s2 += s1 += localBufferPtr[4];
- s2 += s1 += localBufferPtr[5];
- s2 += s1 += localBufferPtr[6];
- s2 += s1 += localBufferPtr[7];
- s2 += s1 += localBufferPtr[8];
- s2 += s1 += localBufferPtr[9];
- s2 += s1 += localBufferPtr[10];
- s2 += s1 += localBufferPtr[11];
- s2 += s1 += localBufferPtr[12];
- s2 += s1 += localBufferPtr[13];
- s2 += s1 += localBufferPtr[14];
- s2 += s1 += localBufferPtr[15];
-
- localBufferPtr += 16;
- length -= 16;
- }
+ HandleLeftOver(localBufferPtr, length, ref s1, ref s2);
+ }
- while (length-- > 0)
- {
- s2 += s1 += *localBufferPtr++;
- }
+ return s1 | (s2 << 16);
+ }
+ }
+ }
- if (s1 >= BASE)
- {
- s1 -= BASE;
- }
+ // Based on: https://github.com/zlib-ng/zlib-ng/blob/develop/arch/x86/adler32_avx2.c
+ [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
+ public static unsafe uint CalculateAvx2(uint adler, ReadOnlySpan buffer)
+ {
+ uint s1 = adler & 0xFFFF;
+ uint s2 = (adler >> 16) & 0xFFFF;
+ uint length = (uint)buffer.Length;
- s2 %= BASE;
+ fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
+ {
+ byte* localBufferPtr = bufferPtr;
+
+ Vector256 zero = Vector256.Zero;
+ var dot3v = Vector256.Create((short)1);
+ var dot2v = Vector256.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
+
+ // Process n blocks of data. At most NMAX data bytes can be
+ // processed before s2 must be reduced modulo BASE.
+ var vs1 = Vector256.CreateScalar(s1);
+ var vs2 = Vector256.CreateScalar(s2);
+
+ while (length >= 32)
+ {
+ int k = length < NMAX ? (int)length : (int)NMAX;
+ k -= k % 32;
+ length -= (uint)k;
+
+ Vector256 vs10 = vs1;
+ Vector256 vs3 = Vector256.Zero;
+
+ while (k >= 32)
+ {
+ // Load 32 input bytes.
+ Vector256 block = Avx.LoadVector256(localBufferPtr);
+
+ // Sum of abs diff, resulting in 2 x int32's
+ Vector256 vs1sad = Avx2.SumAbsoluteDifferences(block, zero);
+
+ vs1 = Avx2.Add(vs1, vs1sad.AsUInt32());
+ vs3 = Avx2.Add(vs3, vs10);
+
+ // sum 32 uint8s to 16 shorts.
+ Vector256 vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v);
+
+ // sum 16 shorts to 8 uint32s.
+ Vector256 vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v);
+
+ vs2 = Avx2.Add(vsum2.AsUInt32(), vs2);
+ vs10 = vs1;
+
+ localBufferPtr += BlockSize;
+ k -= 32;
}
- return s1 | (s2 << 16);
+ // Defer the multiplication with 32 to outside of the loop.
+ vs3 = Avx2.ShiftLeftLogical(vs3, 5);
+ vs2 = Avx2.Add(vs2, vs3);
+
+ s1 = (uint)Numerics.EvenReduceSum(vs1.AsInt32());
+ s2 = (uint)Numerics.ReduceSum(vs2.AsInt32());
+
+ s1 %= BASE;
+ s2 %= BASE;
+
+ vs1 = Vector256.CreateScalar(s1);
+ vs2 = Vector256.CreateScalar(s2);
}
+
+ if (length > 0)
+ {
+ HandleLeftOver(localBufferPtr, length, ref s1, ref s2);
+ }
+
+ return s1 | (s2 << 16);
+ }
+ }
+
+ private static unsafe void HandleLeftOver(byte* localBufferPtr, uint length, ref uint s1, ref uint s2)
+ {
+ if (length >= 16)
+ {
+ s2 += s1 += localBufferPtr[0];
+ s2 += s1 += localBufferPtr[1];
+ s2 += s1 += localBufferPtr[2];
+ s2 += s1 += localBufferPtr[3];
+ s2 += s1 += localBufferPtr[4];
+ s2 += s1 += localBufferPtr[5];
+ s2 += s1 += localBufferPtr[6];
+ s2 += s1 += localBufferPtr[7];
+ s2 += s1 += localBufferPtr[8];
+ s2 += s1 += localBufferPtr[9];
+ s2 += s1 += localBufferPtr[10];
+ s2 += s1 += localBufferPtr[11];
+ s2 += s1 += localBufferPtr[12];
+ s2 += s1 += localBufferPtr[13];
+ s2 += s1 += localBufferPtr[14];
+ s2 += s1 += localBufferPtr[15];
+
+ localBufferPtr += 16;
+ length -= 16;
}
+
+ while (length-- > 0)
+ {
+ s2 += s1 += *localBufferPtr++;
+ }
+
+ if (s1 >= BASE)
+ {
+ s1 -= BASE;
+ }
+
+ s2 %= BASE;
}
#endif
diff --git a/src/ImageSharp/Compression/Zlib/Crc32.Lut.cs b/src/ImageSharp/Compression/Zlib/Crc32.Lut.cs
index 059bd9f31..304372d2d 100644
--- a/src/ImageSharp/Compression/Zlib/Crc32.Lut.cs
+++ b/src/ImageSharp/Compression/Zlib/Crc32.Lut.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Compression.Zlib
{
diff --git a/src/ImageSharp/Compression/Zlib/Crc32.cs b/src/ImageSharp/Compression/Zlib/Crc32.cs
index 075d6112a..0d900cc17 100644
--- a/src/ImageSharp/Compression/Zlib/Crc32.cs
+++ b/src/ImageSharp/Compression/Zlib/Crc32.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Compression/Zlib/DeflateCompressionLevel.cs b/src/ImageSharp/Compression/Zlib/DeflateCompressionLevel.cs
index 2edf76e7d..e483435f6 100644
--- a/src/ImageSharp/Compression/Zlib/DeflateCompressionLevel.cs
+++ b/src/ImageSharp/Compression/Zlib/DeflateCompressionLevel.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Compression.Zlib
{
diff --git a/src/ImageSharp/Compression/Zlib/DeflateThrowHelper.cs b/src/ImageSharp/Compression/Zlib/DeflateThrowHelper.cs
index 02590ca25..d334a0244 100644
--- a/src/ImageSharp/Compression/Zlib/DeflateThrowHelper.cs
+++ b/src/ImageSharp/Compression/Zlib/DeflateThrowHelper.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Compression/Zlib/Deflater.cs b/src/ImageSharp/Compression/Zlib/Deflater.cs
index 7ff8342aa..78736d620 100644
--- a/src/ImageSharp/Compression/Zlib/Deflater.cs
+++ b/src/ImageSharp/Compression/Zlib/Deflater.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Compression/Zlib/DeflaterConstants.cs b/src/ImageSharp/Compression/Zlib/DeflaterConstants.cs
index 30bd75ffc..507952fd3 100644
--- a/src/ImageSharp/Compression/Zlib/DeflaterConstants.cs
+++ b/src/ImageSharp/Compression/Zlib/DeflaterConstants.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
//
using System;
diff --git a/src/ImageSharp/Compression/Zlib/DeflaterEngine.cs b/src/ImageSharp/Compression/Zlib/DeflaterEngine.cs
index 506b0f2c1..a77c22bf8 100644
--- a/src/ImageSharp/Compression/Zlib/DeflaterEngine.cs
+++ b/src/ImageSharp/Compression/Zlib/DeflaterEngine.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
@@ -483,18 +483,21 @@ namespace SixLabors.ImageSharp.Compression.Zlib
int niceLength = Math.Min(this.niceLength, this.lookahead);
int matchStrt = this.matchStart;
- this.matchLen = Math.Max(this.matchLen, DeflaterConstants.MIN_MATCH - 1);
int matchLength = this.matchLen;
+ matchLength = Math.Max(matchLength, DeflaterConstants.MIN_MATCH - 1);
+ this.matchLen = matchLength;
- if (scan + matchLength > scanMax)
+ if (scan > scanMax - matchLength)
{
return false;
}
+ int scanEndPosition = scan + matchLength;
+
byte* pinnedWindow = this.pinnedWindowPointer;
int scanStart = this.strstart;
- byte scanEnd1 = pinnedWindow[scan + matchLength - 1];
- byte scanEnd = pinnedWindow[scan + matchLength];
+ byte scanEnd1 = pinnedWindow[scanEndPosition - 1];
+ byte scanEnd = pinnedWindow[scanEndPosition];
// Do not waste too much time if we already have a good match:
if (matchLength >= this.goodLength)
@@ -508,8 +511,9 @@ namespace SixLabors.ImageSharp.Compression.Zlib
match = curMatch;
scan = scanStart;
- if (pinnedWindow[match + matchLength] != scanEnd
- || pinnedWindow[match + matchLength - 1] != scanEnd1
+ int matchEndPosition = match + matchLength;
+ if (pinnedWindow[matchEndPosition] != scanEnd
+ || pinnedWindow[matchEndPosition - 1] != scanEnd1
|| pinnedWindow[match] != pinnedWindow[scan]
|| pinnedWindow[++match] != pinnedWindow[++scan])
{
@@ -685,6 +689,7 @@ namespace SixLabors.ImageSharp.Compression.Zlib
return false;
}
+ const int windowLen = (2 * DeflaterConstants.WSIZE) - DeflaterConstants.MIN_LOOKAHEAD;
while (this.lookahead >= DeflaterConstants.MIN_LOOKAHEAD || flush)
{
if (this.lookahead == 0)
@@ -695,7 +700,7 @@ namespace SixLabors.ImageSharp.Compression.Zlib
return false;
}
- if (this.strstart > (2 * DeflaterConstants.WSIZE) - DeflaterConstants.MIN_LOOKAHEAD)
+ if (this.strstart > windowLen)
{
// slide window, as FindLongestMatch needs this.
// This should only happen when flushing and the window
@@ -766,6 +771,7 @@ namespace SixLabors.ImageSharp.Compression.Zlib
return false;
}
+ const int windowLen = (2 * DeflaterConstants.WSIZE) - DeflaterConstants.MIN_LOOKAHEAD;
while (this.lookahead >= DeflaterConstants.MIN_LOOKAHEAD || flush)
{
if (this.lookahead == 0)
@@ -783,7 +789,7 @@ namespace SixLabors.ImageSharp.Compression.Zlib
return false;
}
- if (this.strstart >= (2 * DeflaterConstants.WSIZE) - DeflaterConstants.MIN_LOOKAHEAD)
+ if (this.strstart >= windowLen)
{
// slide window, as FindLongestMatch needs this.
// This should only happen when flushing and the window
diff --git a/src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs b/src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs
index 27a8d5671..653660112 100644
--- a/src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs
+++ b/src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
diff --git a/src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs b/src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs
index d949ddf38..9ba9d34b1 100644
--- a/src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs
+++ b/src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
diff --git a/src/ImageSharp/Compression/Zlib/DeflaterPendingBuffer.cs b/src/ImageSharp/Compression/Zlib/DeflaterPendingBuffer.cs
index 8f2c8d398..b24150faf 100644
--- a/src/ImageSharp/Compression/Zlib/DeflaterPendingBuffer.cs
+++ b/src/ImageSharp/Compression/Zlib/DeflaterPendingBuffer.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
diff --git a/src/ImageSharp/Compression/Zlib/ZlibDeflateStream.cs b/src/ImageSharp/Compression/Zlib/ZlibDeflateStream.cs
index 44883665a..5bccf470d 100644
--- a/src/ImageSharp/Compression/Zlib/ZlibDeflateStream.cs
+++ b/src/ImageSharp/Compression/Zlib/ZlibDeflateStream.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.IO;
diff --git a/src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs
index f4b0543b8..ed5766435 100644
--- a/src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs
+++ b/src/ImageSharp/Compression/Zlib/ZlibInflateStream.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.IO;
diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs
index b19d31b4e..c4efe8a08 100644
--- a/src/ImageSharp/Configuration.cs
+++ b/src/ImageSharp/Configuration.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Collections.Concurrent;
diff --git a/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs b/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
index d83e737f1..274486cec 100644
--- a/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
+++ b/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
+using System;
using System.Threading;
namespace SixLabors.ImageSharp.Diagnostics
@@ -47,6 +48,16 @@ namespace SixLabors.ImageSharp.Diagnostics
}
}
+ ///
+ /// Fires when ImageSharp allocates memory from a MemoryAllocator
+ ///
+ internal static event Action MemoryAllocated;
+
+ ///
+ /// Fires when ImageSharp releases memory allocated from a MemoryAllocator
+ ///
+ internal static event Action MemoryReleased;
+
///
/// Gets a value indicating the total number of memory resource objects leaked to the finalizer.
///
@@ -54,11 +65,17 @@ namespace SixLabors.ImageSharp.Diagnostics
internal static bool UndisposedAllocationSubscribed => Volatile.Read(ref undisposedAllocationSubscriptionCounter) > 0;
- internal static void IncrementTotalUndisposedAllocationCount() =>
+ internal static void IncrementTotalUndisposedAllocationCount()
+ {
Interlocked.Increment(ref totalUndisposedAllocationCount);
+ MemoryAllocated?.Invoke();
+ }
- internal static void DecrementTotalUndisposedAllocationCount() =>
+ internal static void DecrementTotalUndisposedAllocationCount()
+ {
Interlocked.Decrement(ref totalUndisposedAllocationCount);
+ MemoryReleased?.Invoke();
+ }
internal static void RaiseUndisposedMemoryResource(string allocationStackTrace)
{
diff --git a/src/ImageSharp/Formats/Bmp/BmpArrayFileHeader.cs b/src/ImageSharp/Formats/Bmp/BmpArrayFileHeader.cs
index d68841ad1..d2e043750 100644
--- a/src/ImageSharp/Formats/Bmp/BmpArrayFileHeader.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpArrayFileHeader.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.InteropServices;
diff --git a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs
index 7801e48a9..1b73d8b18 100644
--- a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Bmp
{
diff --git a/src/ImageSharp/Formats/Bmp/BmpColorSpace.cs b/src/ImageSharp/Formats/Bmp/BmpColorSpace.cs
new file mode 100644
index 000000000..0bab53a2b
--- /dev/null
+++ b/src/ImageSharp/Formats/Bmp/BmpColorSpace.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+// ReSharper disable InconsistentNaming
+namespace SixLabors.ImageSharp.Formats.Bmp
+{
+ ///
+ /// Enum for the different color spaces.
+ ///
+ internal enum BmpColorSpace
+ {
+ ///
+ /// This value implies that endpoints and gamma values are given in the appropriate fields.
+ ///
+ LCS_CALIBRATED_RGB = 0,
+
+ ///
+ /// The Windows default color space ('Win ').
+ ///
+ LCS_WINDOWS_COLOR_SPACE = 1466527264,
+
+ ///
+ /// Specifies that the bitmap is in sRGB color space ('sRGB').
+ ///
+ LCS_sRGB = 1934772034,
+
+ ///
+ /// This value indicates that bV5ProfileData points to the file name of the profile to use (gamma and endpoints values are ignored).
+ ///
+ PROFILE_LINKED = 1279872587,
+
+ ///
+ /// This value indicates that bV5ProfileData points to a memory buffer that contains the profile to be used (gamma and endpoints values are ignored).
+ ///
+ PROFILE_EMBEDDED = 1296188740
+ }
+}
diff --git a/src/ImageSharp/Formats/Bmp/BmpCompression.cs b/src/ImageSharp/Formats/Bmp/BmpCompression.cs
index 50d1ae46d..2749462e3 100644
--- a/src/ImageSharp/Formats/Bmp/BmpCompression.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpCompression.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Bmp
{
diff --git a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs
index 0bec34ffb..cff81d58e 100644
--- a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Bmp
{
diff --git a/src/ImageSharp/Formats/Bmp/BmpConstants.cs b/src/ImageSharp/Formats/Bmp/BmpConstants.cs
index 4684406b2..9c200e429 100644
--- a/src/ImageSharp/Formats/Bmp/BmpConstants.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpConstants.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
index 4c433b203..15d5b9a08 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
@@ -1,9 +1,8 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp
@@ -29,48 +28,25 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } = RleSkippedPixelHandling.Black;
///
- public Image Decode(Configuration configuration, Stream stream)
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(stream, nameof(stream));
var decoder = new BmpDecoderCore(configuration, this);
- return decoder.Decode(configuration, stream);
+ return decoder.Decode(configuration, stream, cancellationToken);
}
///
- public Image Decode(Configuration configuration, Stream stream)
- => this.Decode(configuration, stream);
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
+ => this.Decode(configuration, stream, cancellationToken);
///
- public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- where TPixel : unmanaged, IPixel
+ public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(stream, nameof(stream));
- var decoder = new BmpDecoderCore(configuration, this);
- return decoder.DecodeAsync(configuration, stream, cancellationToken);
- }
-
- ///
- public async Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- => await this.DecodeAsync(configuration, stream, cancellationToken)
- .ConfigureAwait(false);
-
- ///
- public IImageInfo Identify(Configuration configuration, Stream stream)
- {
- Guard.NotNull(stream, nameof(stream));
-
- return new BmpDecoderCore(configuration, this).Identify(configuration, stream);
- }
-
- ///
- public Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- {
- Guard.NotNull(stream, nameof(stream));
-
- return new BmpDecoderCore(configuration, this).IdentifyAsync(configuration, stream, cancellationToken);
+ return new BmpDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken);
}
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index fe31a686b..3a96c4022 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
@@ -11,6 +11,7 @@ using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
+using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp
@@ -122,11 +123,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
+ Image image = null;
try
{
int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
- var image = new Image(this.Configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata);
+ image = new Image(this.Configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata);
Buffer2D pixels = image.GetRootFramePixelBuffer();
@@ -184,7 +186,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
break;
default:
- BmpThrowHelper.ThrowNotSupportedException("Does not support this kind of bitmap files.");
+ BmpThrowHelper.ThrowNotSupportedException("ImageSharp does not support this kind of bitmap files.");
break;
}
@@ -193,8 +195,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
catch (IndexOutOfRangeException e)
{
+ image?.Dispose();
throw new ImageFormatException("Bitmap does not have a valid format.", e);
}
+ catch
+ {
+ image?.Dispose();
+ throw;
+ }
}
///
@@ -323,12 +331,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4]));
break;
case RleSkippedPixelHandling.Transparent:
- color.FromVector4(Vector4.Zero);
+ color.FromScaledVector4(Vector4.Zero);
break;
// Default handling for skipped pixels is black (which is what System.Drawing is also doing).
default:
- color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
+ color.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
break;
}
}
@@ -382,7 +390,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
if (rowHasUndefinedPixels)
{
// Slow path with undefined pixels.
- var yMulWidth = y * width;
+ int yMulWidth = y * width;
int rowStartIdx = yMulWidth * 3;
for (int x = 0; x < width; x++)
{
@@ -395,12 +403,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
color.FromBgr24(Unsafe.As(ref bufferSpan[idx]));
break;
case RleSkippedPixelHandling.Transparent:
- color.FromVector4(Vector4.Zero);
+ color.FromScaledVector4(Vector4.Zero);
break;
// Default handling for skipped pixels is black (which is what System.Drawing is also doing).
default:
- color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
+ color.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
break;
}
}
@@ -1127,7 +1135,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
g * invMaxValueGreen,
b * invMaxValueBlue,
alpha);
- color.FromVector4(vector4);
+ color.FromScaledVector4(vector4);
}
else
{
@@ -1192,6 +1200,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private void ReadInfoHeader()
{
Span buffer = stackalloc byte[BmpInfoHeader.MaxHeaderSize];
+ long infoHeaderStart = this.stream.Position;
+
+ // Resolution is stored in PPM.
+ this.metadata = new ImageMetadata
+ {
+ ResolutionUnits = PixelResolutionUnit.PixelsPerMeter
+ };
// Read the header size.
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
@@ -1264,36 +1279,45 @@ namespace SixLabors.ImageSharp.Formats.Bmp
infoHeaderType = BmpInfoHeaderType.Os2Version2;
this.infoHeader = BmpInfoHeader.ParseOs2Version2(buffer);
}
- else if (headerSize >= BmpInfoHeader.SizeV4)
+ else if (headerSize == BmpInfoHeader.SizeV4)
{
- // >= 108 bytes
- infoHeaderType = headerSize == BmpInfoHeader.SizeV4 ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion5;
+ // == 108 bytes
+ infoHeaderType = BmpInfoHeaderType.WinVersion4;
this.infoHeader = BmpInfoHeader.ParseV4(buffer);
}
+ else if (headerSize > BmpInfoHeader.SizeV4)
+ {
+ // > 108 bytes
+ infoHeaderType = BmpInfoHeaderType.WinVersion5;
+ this.infoHeader = BmpInfoHeader.ParseV5(buffer);
+ if (this.infoHeader.ProfileData != 0 && this.infoHeader.ProfileSize != 0)
+ {
+ // Read color profile.
+ long streamPosition = this.stream.Position;
+ byte[] iccProfileData = new byte[this.infoHeader.ProfileSize];
+ this.stream.Position = infoHeaderStart + this.infoHeader.ProfileData;
+ this.stream.Read(iccProfileData);
+ this.metadata.IccProfile = new IccProfile(iccProfileData);
+ this.stream.Position = streamPosition;
+ }
+ }
else
{
BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize '{headerSize}'.");
}
- // Resolution is stored in PPM.
- var meta = new ImageMetadata
- {
- ResolutionUnits = PixelResolutionUnit.PixelsPerMeter
- };
if (this.infoHeader.XPelsPerMeter > 0 && this.infoHeader.YPelsPerMeter > 0)
{
- meta.HorizontalResolution = this.infoHeader.XPelsPerMeter;
- meta.VerticalResolution = this.infoHeader.YPelsPerMeter;
+ this.metadata.HorizontalResolution = this.infoHeader.XPelsPerMeter;
+ this.metadata.VerticalResolution = this.infoHeader.YPelsPerMeter;
}
else
{
// Convert default metadata values to PPM.
- meta.HorizontalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetadata.DefaultHorizontalResolution));
- meta.VerticalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetadata.DefaultVerticalResolution));
+ this.metadata.HorizontalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetadata.DefaultHorizontalResolution));
+ this.metadata.VerticalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetadata.DefaultVerticalResolution));
}
- this.metadata = meta;
-
short bitsPerPixel = this.infoHeader.BitsPerPixel;
this.bmpMetadata = this.metadata.GetBmpMetadata();
this.bmpMetadata.InfoHeaderType = infoHeaderType;
@@ -1363,9 +1387,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int colorMapSizeBytes = -1;
if (this.infoHeader.ClrUsed == 0)
{
- if (this.infoHeader.BitsPerPixel == 1
- || this.infoHeader.BitsPerPixel == 4
- || this.infoHeader.BitsPerPixel == 8)
+ if (this.infoHeader.BitsPerPixel is 1 or 4 or 8)
{
switch (this.fileMarkerType)
{
@@ -1417,7 +1439,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int skipAmount = this.fileHeader.Offset - (int)this.stream.Position;
if ((skipAmount + (int)this.stream.Position) > this.stream.Length)
{
- BmpThrowHelper.ThrowInvalidImageContentException("Invalid fileheader offset found. Offset is greater than the stream length.");
+ BmpThrowHelper.ThrowInvalidImageContentException("Invalid file header offset found. Offset is greater than the stream length.");
}
if (skipAmount > 0)
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs
index f256ed9f8..25669b3f9 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
index 6384074df..f71275b7c 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -1,8 +1,9 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
+using System.Buffers.Binary;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
@@ -79,9 +80,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
/// A bitmap v4 header will only be written, if the user explicitly wants support for transparency.
/// In this case the compression type BITFIELDS will be used.
+ /// If the image contains a color profile, a bitmap v5 header is written, which is needed to write this info.
/// Otherwise a bitmap v3 header will be written, which is supported by almost all decoders.
///
- private readonly bool writeV4Header;
+ private BmpInfoHeaderType infoHeaderType;
///
/// The quantizer for reducing the color count for 8-Bit, 4-Bit and 1-Bit images.
@@ -97,8 +99,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
this.memoryAllocator = memoryAllocator;
this.bitsPerPixel = options.BitsPerPixel;
- this.writeV4Header = options.SupportTransparency;
this.quantizer = options.Quantizer ?? KnownQuantizers.Octree;
+ this.infoHeaderType = options.SupportTransparency ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion3;
}
///
@@ -123,7 +125,62 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);
this.padding = bytesPerLine - (int)(image.Width * (bpp / 8F));
- // Set Resolution.
+ int colorPaletteSize = 0;
+ if (this.bitsPerPixel == BmpBitsPerPixel.Pixel8)
+ {
+ colorPaletteSize = ColorPaletteSize8Bit;
+ }
+ else if (this.bitsPerPixel == BmpBitsPerPixel.Pixel4)
+ {
+ colorPaletteSize = ColorPaletteSize4Bit;
+ }
+ else if (this.bitsPerPixel == BmpBitsPerPixel.Pixel1)
+ {
+ colorPaletteSize = ColorPaletteSize1Bit;
+ }
+
+ byte[] iccProfileData = null;
+ int iccProfileSize = 0;
+ if (metadata.IccProfile != null)
+ {
+ this.infoHeaderType = BmpInfoHeaderType.WinVersion5;
+ iccProfileData = metadata.IccProfile.ToByteArray();
+ iccProfileSize = iccProfileData.Length;
+ }
+
+ int infoHeaderSize = this.infoHeaderType switch
+ {
+ BmpInfoHeaderType.WinVersion3 => BmpInfoHeader.SizeV3,
+ BmpInfoHeaderType.WinVersion4 => BmpInfoHeader.SizeV4,
+ BmpInfoHeaderType.WinVersion5 => BmpInfoHeader.SizeV5,
+ _ => BmpInfoHeader.SizeV3
+ };
+
+ BmpInfoHeader infoHeader = this.CreateBmpInfoHeader(image.Width, image.Height, infoHeaderSize, bpp, bytesPerLine, metadata, iccProfileData);
+
+ Span buffer = stackalloc byte[infoHeaderSize];
+
+ this.WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer);
+ this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize);
+ this.WriteImage(stream, image.Frames.RootFrame);
+ this.WriteColorProfile(stream, iccProfileData, buffer);
+
+ stream.Flush();
+ }
+
+ ///
+ /// Creates the bitmap information header.
+ ///
+ /// The width of the image.
+ /// The height of the image.
+ /// Size of the information header.
+ /// The bits per pixel.
+ /// The bytes per line.
+ /// The metadata.
+ /// The icc profile data.
+ /// The bitmap information header.
+ private BmpInfoHeader CreateBmpInfoHeader(int width, int height, int infoHeaderSize, short bpp, int bytesPerLine, ImageMetadata metadata, byte[] iccProfileData)
+ {
int hResolution = 0;
int vResolution = 0;
@@ -154,20 +211,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
}
- int infoHeaderSize = this.writeV4Header ? BmpInfoHeader.SizeV4 : BmpInfoHeader.SizeV3;
var infoHeader = new BmpInfoHeader(
headerSize: infoHeaderSize,
- height: image.Height,
- width: image.Width,
+ height: height,
+ width: width,
bitsPerPixel: bpp,
planes: 1,
- imageSize: image.Height * bytesPerLine,
+ imageSize: height * bytesPerLine,
clrUsed: 0,
clrImportant: 0,
xPelsPerMeter: hResolution,
yPelsPerMeter: vResolution);
- if (this.writeV4Header && this.bitsPerPixel == BmpBitsPerPixel.Pixel32)
+ if ((this.infoHeaderType is BmpInfoHeaderType.WinVersion4 or BmpInfoHeaderType.WinVersion5) && this.bitsPerPixel == BmpBitsPerPixel.Pixel32)
{
infoHeader.AlphaMask = Rgba32AlphaMask;
infoHeader.RedMask = Rgba32RedMask;
@@ -176,45 +232,79 @@ namespace SixLabors.ImageSharp.Formats.Bmp
infoHeader.Compression = BmpCompression.BitFields;
}
- int colorPaletteSize = 0;
- if (this.bitsPerPixel == BmpBitsPerPixel.Pixel8)
+ if (this.infoHeaderType is BmpInfoHeaderType.WinVersion5 && metadata.IccProfile != null)
{
- colorPaletteSize = ColorPaletteSize8Bit;
+ infoHeader.ProfileSize = iccProfileData.Length;
+ infoHeader.CsType = BmpColorSpace.PROFILE_EMBEDDED;
+ infoHeader.Intent = BmpRenderingIntent.LCS_GM_IMAGES;
}
- else if (this.bitsPerPixel == BmpBitsPerPixel.Pixel4)
- {
- colorPaletteSize = ColorPaletteSize4Bit;
- }
- else if (this.bitsPerPixel == BmpBitsPerPixel.Pixel1)
+
+ return infoHeader;
+ }
+
+ ///
+ /// Writes the color profile to the stream.
+ ///
+ /// The stream to write to.
+ /// The color profile data.
+ /// The buffer.
+ private void WriteColorProfile(Stream stream, byte[] iccProfileData, Span buffer)
+ {
+ if (iccProfileData != null)
{
- colorPaletteSize = ColorPaletteSize1Bit;
+ // The offset, in bytes, from the beginning of the BITMAPV5HEADER structure to the start of the profile data.
+ int streamPositionAfterImageData = (int)stream.Position - BmpFileHeader.Size;
+ stream.Write(iccProfileData);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer, streamPositionAfterImageData);
+ stream.Position = BmpFileHeader.Size + 112;
+ stream.Write(buffer.Slice(0, 4));
}
+ }
+ ///
+ /// Writes the bitmap file header.
+ ///
+ /// The stream to write the header to.
+ /// Size of the bitmap information header.
+ /// Size of the color palette.
+ /// The size in bytes of the color profile.
+ /// The information header to write.
+ /// The buffer to write to.
+ private void WriteBitmapFileHeader(Stream stream, int infoHeaderSize, int colorPaletteSize, int iccProfileSize, BmpInfoHeader infoHeader, Span buffer)
+ {
var fileHeader = new BmpFileHeader(
type: BmpConstants.TypeMarkers.Bitmap,
- fileSize: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize + infoHeader.ImageSize,
+ fileSize: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize + iccProfileSize + infoHeader.ImageSize,
reserved: 0,
offset: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize);
- Span buffer = stackalloc byte[infoHeaderSize];
fileHeader.WriteTo(buffer);
-
stream.Write(buffer, 0, BmpFileHeader.Size);
+ }
- if (this.writeV4Header)
- {
- infoHeader.WriteV4Header(buffer);
- }
- else
+ ///
+ /// Writes the bitmap information header.
+ ///
+ /// The stream to write info header into.
+ /// The information header.
+ /// The buffer.
+ /// Size of the information header.
+ private void WriteBitmapInfoHeader(Stream stream, BmpInfoHeader infoHeader, Span buffer, int infoHeaderSize)
+ {
+ switch (this.infoHeaderType)
{
- infoHeader.WriteV3Header(buffer);
+ case BmpInfoHeaderType.WinVersion3:
+ infoHeader.WriteV3Header(buffer);
+ break;
+ case BmpInfoHeaderType.WinVersion4:
+ infoHeader.WriteV4Header(buffer);
+ break;
+ case BmpInfoHeaderType.WinVersion5:
+ infoHeader.WriteV5Header(buffer);
+ break;
}
stream.Write(buffer, 0, infoHeaderSize);
-
- this.WriteImage(stream, image.Frames.RootFrame);
-
- stream.Flush();
}
///
diff --git a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs b/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
index ab56bd246..25254d210 100644
--- a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Bmp/BmpFileMarkerType.cs b/src/ImageSharp/Formats/Bmp/BmpFileMarkerType.cs
index 882ccd12a..eb6640ba9 100644
--- a/src/ImageSharp/Formats/Bmp/BmpFileMarkerType.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpFileMarkerType.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Bmp
{
diff --git a/src/ImageSharp/Formats/Bmp/BmpFormat.cs b/src/ImageSharp/Formats/Bmp/BmpFormat.cs
index 2b24c4387..f0d62f1d4 100644
--- a/src/ImageSharp/Formats/Bmp/BmpFormat.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpFormat.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
diff --git a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
index 82376a30c..897b65121 100644
--- a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
index 6cd9d19f5..823569b8a 100644
--- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
@@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int greenMask = 0,
int blueMask = 0,
int alphaMask = 0,
- int csType = 0,
+ BmpColorSpace csType = 0,
int redX = 0,
int redY = 0,
int redZ = 0,
@@ -94,7 +94,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int blueZ = 0,
int gammeRed = 0,
int gammeGreen = 0,
- int gammeBlue = 0)
+ int gammeBlue = 0,
+ BmpRenderingIntent intent = BmpRenderingIntent.Invalid,
+ int profileData = 0,
+ int profileSize = 0,
+ int reserved = 0)
{
this.HeaderSize = headerSize;
this.Width = width;
@@ -124,6 +128,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.GammaRed = gammeRed;
this.GammaGreen = gammeGreen;
this.GammaBlue = gammeBlue;
+ this.Intent = intent;
+ this.ProfileData = profileData;
+ this.ProfileSize = profileSize;
+ this.Reserved = reserved;
}
///
@@ -211,7 +219,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
/// Gets or sets the Color space type. Not used yet.
///
- public int CsType { get; set; }
+ public BmpColorSpace CsType { get; set; }
///
/// Gets or sets the X coordinate of red endpoint. Not used yet.
@@ -273,6 +281,26 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
public int GammaBlue { get; set; }
+ ///
+ /// Gets or sets the rendering intent for bitmap.
+ ///
+ public BmpRenderingIntent Intent { get; set; }
+
+ ///
+ /// Gets or sets the offset, in bytes, from the beginning of the BITMAPV5HEADER structure to the start of the profile data.
+ ///
+ public int ProfileData { get; set; }
+
+ ///
+ /// Gets or sets the size, in bytes, of embedded profile data.
+ ///
+ public int ProfileSize { get; set; }
+
+ ///
+ /// Gets or sets the reserved value.
+ ///
+ public int Reserved { get; set; }
+
///
/// Parses the BITMAPCOREHEADER (BMP Version 2) consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes).
///
@@ -401,11 +429,47 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// The data to parse.
/// The parsed header.
///
- public static BmpInfoHeader ParseV4(ReadOnlySpan data)
+ public static BmpInfoHeader ParseV4(ReadOnlySpan data) => new(
+ headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
+ width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
+ height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
+ planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
+ bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)),
+ compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)),
+ imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)),
+ xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)),
+ yPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)),
+ clrUsed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)),
+ clrImportant: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)),
+ redMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(40, 4)),
+ greenMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(44, 4)),
+ blueMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(48, 4)),
+ alphaMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(52, 4)),
+ csType: (BmpColorSpace)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(56, 4)),
+ redX: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(60, 4)),
+ redY: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(64, 4)),
+ redZ: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(68, 4)),
+ greenX: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(72, 4)),
+ greenY: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(76, 4)),
+ greenZ: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(80, 4)),
+ blueX: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(84, 4)),
+ blueY: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(88, 4)),
+ blueZ: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(92, 4)),
+ gammeRed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(96, 4)),
+ gammeGreen: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(100, 4)),
+ gammeBlue: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(104, 4)));
+
+ ///
+ /// Parses the full BMP Version 5 BITMAPINFOHEADER header (124 bytes).
+ ///
+ /// The data to parse.
+ /// The parsed header.
+ ///
+ public static BmpInfoHeader ParseV5(ReadOnlySpan data)
{
- if (data.Length < SizeV4)
+ if (data.Length < SizeV5)
{
- throw new ArgumentException(nameof(data), $"Must be {SizeV4} bytes. Was {data.Length} bytes.");
+ throw new ArgumentException(nameof(data), $"Must be {SizeV5} bytes. Was {data.Length} bytes.");
}
return MemoryMarshal.Cast(data)[0];
@@ -436,6 +500,43 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
/// The buffer to write to.
public void WriteV4Header(Span buffer)
+ {
+ buffer.Clear();
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(0, 4), SizeV4);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(4, 4), this.Width);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(8, 4), this.Height);
+ BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(12, 2), this.Planes);
+ BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(14, 2), this.BitsPerPixel);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(16, 4), (int)this.Compression);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(20, 4), this.ImageSize);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(24, 4), this.XPelsPerMeter);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(28, 4), this.YPelsPerMeter);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(32, 4), this.ClrUsed);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(36, 4), this.ClrImportant);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(40, 4), this.RedMask);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(44, 4), this.GreenMask);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(48, 4), this.BlueMask);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(52, 4), this.AlphaMask);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(56, 4), (int)this.CsType);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(60, 4), this.RedX);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(64, 4), this.RedY);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(68, 4), this.RedZ);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(72, 4), this.GreenX);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(76, 4), this.GreenY);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(80, 4), this.GreenZ);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(84, 4), this.BlueX);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(88, 4), this.BlueY);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(92, 4), this.BlueZ);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(96, 4), this.GammaRed);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(100, 4), this.GammaGreen);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(104, 4), this.GammaBlue);
+ }
+
+ ///
+ /// Writes a complete Bitmap V5 header to a buffer.
+ ///
+ /// The buffer to write to.
+ public void WriteV5Header(Span buffer)
{
ref BmpInfoHeader dest = ref Unsafe.As(ref MemoryMarshal.GetReference(buffer));
diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs
index 86bfdf9bf..34b063ae4 100644
--- a/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Bmp
{
diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs
index b7b668a7a..4a0725a7a 100644
--- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Bmp
{
diff --git a/src/ImageSharp/Formats/Bmp/BmpRenderingIntent.cs b/src/ImageSharp/Formats/Bmp/BmpRenderingIntent.cs
new file mode 100644
index 000000000..2000a2794
--- /dev/null
+++ b/src/ImageSharp/Formats/Bmp/BmpRenderingIntent.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+// ReSharper disable InconsistentNaming
+namespace SixLabors.ImageSharp.Formats.Bmp
+{
+ ///
+ /// Enum for the different rendering intent's.
+ ///
+ internal enum BmpRenderingIntent
+ {
+ ///
+ /// Invalid default value.
+ ///
+ Invalid = 0,
+
+ ///
+ /// Maintains saturation. Used for business charts and other situations in which undithered colors are required.
+ ///
+ LCS_GM_BUSINESS = 1,
+
+ ///
+ /// Maintains colorimetric match. Used for graphic designs and named colors.
+ ///
+ LCS_GM_GRAPHICS = 2,
+
+ ///
+ /// Maintains contrast. Used for photographs and natural images.
+ ///
+ LCS_GM_IMAGES = 4,
+
+ ///
+ /// Maintains the white point. Matches the colors to their nearest color in the destination gamut.
+ ///
+ LCS_GM_ABS_COLORIMETRIC = 8,
+ }
+}
diff --git a/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs
index 3d577d278..5fa760b8e 100644
--- a/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
index ff88d15a3..1283d0ae1 100644
--- a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
+++ b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Bmp
{
diff --git a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs
index 30aa70452..9c035119d 100644
--- a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Quantization;
diff --git a/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs b/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs
index a6f212e2e..5c7f9c2d4 100644
--- a/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Metadata;
diff --git a/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs b/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs
index ce113dce7..4e23947c1 100644
--- a/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs
+++ b/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Bmp
{
diff --git a/src/ImageSharp/Formats/Gif/GifColorTableMode.cs b/src/ImageSharp/Formats/Gif/GifColorTableMode.cs
index b8569a321..a33dc1707 100644
--- a/src/ImageSharp/Formats/Gif/GifColorTableMode.cs
+++ b/src/ImageSharp/Formats/Gif/GifColorTableMode.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Gif
{
diff --git a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs
index 8f846f9d5..108026ffe 100644
--- a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs
+++ b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Gif
{
diff --git a/src/ImageSharp/Formats/Gif/GifConstants.cs b/src/ImageSharp/Formats/Gif/GifConstants.cs
index 1179b67b1..4aa18bacb 100644
--- a/src/ImageSharp/Formats/Gif/GifConstants.cs
+++ b/src/ImageSharp/Formats/Gif/GifConstants.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Collections.Generic;
diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs
index 196d77ad7..666c3b92c 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs
@@ -1,11 +1,8 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
-using SixLabors.ImageSharp.IO;
-using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@@ -27,48 +24,24 @@ namespace SixLabors.ImageSharp.Formats.Gif
public FrameDecodingMode DecodingMode { get; set; } = FrameDecodingMode.All;
///
- public Image Decode(Configuration configuration, Stream stream)
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
var decoder = new GifDecoderCore(configuration, this);
- return decoder.Decode(configuration, stream);
+ return decoder.Decode(configuration, stream, cancellationToken);
}
///
- public Image Decode(Configuration configuration, Stream stream)
- => this.Decode(configuration, stream);
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
+ => this.Decode(configuration, stream, cancellationToken);
///
- public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- where TPixel : unmanaged, IPixel
- {
- var decoder = new GifDecoderCore(configuration, this);
- return decoder.DecodeAsync(configuration, stream, cancellationToken);
- }
-
- ///
- public async Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- => await this.DecodeAsync(configuration, stream, cancellationToken)
- .ConfigureAwait(false);
-
- ///
- public IImageInfo Identify(Configuration configuration, Stream stream)
- {
- Guard.NotNull(stream, nameof(stream));
-
- var decoder = new GifDecoderCore(configuration, this);
-
- using var bufferedStream = new BufferedReadStream(configuration, stream);
- return decoder.Identify(bufferedStream, default);
- }
-
- ///
- public Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
+ public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(stream, nameof(stream));
var decoder = new GifDecoderCore(configuration, this);
- return decoder.IdentifyAsync(configuration, stream, cancellationToken);
+ return decoder.Identify(configuration, stream, cancellationToken);
}
}
}
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index 72e3ed144..25354f6f3 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
@@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Gets the dimensions of the image.
///
- public Size Dimensions => new Size(this.imageDescriptor.Width, this.imageDescriptor.Height);
+ public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height);
private MemoryAllocator MemoryAllocator => this.Configuration.MemoryAllocator;
@@ -221,7 +221,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
private void ReadGraphicalControlExtension()
{
- this.stream.Read(this.buffer, 0, 6);
+ int bytesRead = this.stream.Read(this.buffer, 0, 6);
+ if (bytesRead != 6)
+ {
+ GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the graphic control extension");
+ }
this.graphicsControlExtension = GifGraphicControlExtension.Parse(this.buffer);
}
@@ -231,7 +235,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
private void ReadImageDescriptor()
{
- this.stream.Read(this.buffer, 0, 9);
+ int bytesRead = this.stream.Read(this.buffer, 0, 9);
+ if (bytesRead != 9)
+ {
+ GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the image descriptor");
+ }
this.imageDescriptor = GifImageDescriptor.Parse(this.buffer);
if (this.imageDescriptor.Height == 0 || this.imageDescriptor.Width == 0)
@@ -245,7 +253,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
private void ReadLogicalScreenDescriptor()
{
- this.stream.Read(this.buffer, 0, 7);
+ int bytesRead = this.stream.Read(this.buffer, 0, 7);
+ if (bytesRead != 7)
+ {
+ GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the logical screen descriptor");
+ }
this.logicalScreenDescriptor = GifLogicalScreenDescriptor.Parse(this.buffer);
}
@@ -265,10 +277,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize);
bool isXmp = this.buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes);
- if (isXmp)
+ if (isXmp && !this.IgnoreMetadata)
{
- var extension = GifXmpApplicationExtension.Read(this.stream);
- this.metadata.XmpProfile = new XmpProfile(extension.Data);
+ var extension = GifXmpApplicationExtension.Read(this.stream, this.MemoryAllocator);
+ if (extension.Data.Length > 0)
+ {
+ this.metadata.XmpProfile = new XmpProfile(extension.Data);
+ }
+
return;
}
else
@@ -374,8 +390,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
indices = this.Configuration.MemoryAllocator.Allocate2D(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean);
-
this.ReadFrameIndices(indices);
+
Span rawColorTable = default;
if (localColorTable != null)
{
@@ -406,9 +422,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadFrameIndices(Buffer2D indices)
{
- int dataSize = this.stream.ReadByte();
+ int minCodeSize = this.stream.ReadByte();
using var lzwDecoder = new LzwDecoder(this.Configuration.MemoryAllocator, this.stream);
- lzwDecoder.DecodePixels(dataSize, indices);
+ lzwDecoder.DecodePixels(minCodeSize, indices);
}
///
diff --git a/src/ImageSharp/Formats/Gif/GifDisposalMethod.cs b/src/ImageSharp/Formats/Gif/GifDisposalMethod.cs
index 2211dfe4b..d5823e44f 100644
--- a/src/ImageSharp/Formats/Gif/GifDisposalMethod.cs
+++ b/src/ImageSharp/Formats/Gif/GifDisposalMethod.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Gif
{
diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs
index 116ee3dae..937e65db9 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index a21b050a8..bb45c8364 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
@@ -123,7 +123,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.WriteComments(gifMetadata, stream);
// Write application extensions.
- this.WriteApplicationExtensions(stream, image.Frames.Count, gifMetadata.RepeatCount, metadata.XmpProfile);
+ XmpProfile xmpProfile = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile;
+ this.WriteApplicationExtensions(stream, image.Frames.Count, gifMetadata.RepeatCount, xmpProfile);
if (useGlobalTable)
{
@@ -137,7 +138,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Clean up.
quantized.Dispose();
- // TODO: Write extension etc
stream.WriteByte(GifConstants.EndIntroducer);
}
@@ -428,26 +428,31 @@ namespace SixLabors.ImageSharp.Formats.Gif
where TGifExtension : struct, IGifExtension
{
IMemoryOwner owner = null;
- Span buffer;
+ Span extensionBuffer;
int extensionSize = extension.ContentLength;
- if (extensionSize > this.buffer.Length - 3)
+
+ if (extensionSize == 0)
+ {
+ return;
+ }
+ else if (extensionSize > this.buffer.Length - 3)
{
owner = this.memoryAllocator.Allocate(extensionSize + 3);
- buffer = owner.GetSpan();
+ extensionBuffer = owner.GetSpan();
}
else
{
- buffer = this.buffer;
+ extensionBuffer = this.buffer;
}
- buffer[0] = GifConstants.ExtensionIntroducer;
- buffer[1] = extension.Label;
+ extensionBuffer[0] = GifConstants.ExtensionIntroducer;
+ extensionBuffer[1] = extension.Label;
- extension.WriteTo(buffer.Slice(2));
+ extension.WriteTo(extensionBuffer.Slice(2));
- buffer[extensionSize + 2] = GifConstants.Terminator;
+ extensionBuffer[extensionSize + 2] = GifConstants.Terminator;
- stream.Write(buffer, 0, extensionSize + 3);
+ stream.Write(extensionBuffer, 0, extensionSize + 3);
owner?.Dispose();
}
diff --git a/src/ImageSharp/Formats/Gif/GifFormat.cs b/src/ImageSharp/Formats/Gif/GifFormat.cs
index 459f0068b..aa05d93cc 100644
--- a/src/ImageSharp/Formats/Gif/GifFormat.cs
+++ b/src/ImageSharp/Formats/Gif/GifFormat.cs
@@ -1,5 +1,5 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Gets the current instance.
///
- public static GifFormat Instance { get; } = new GifFormat();
+ public static GifFormat Instance { get; } = new();
///
public string Name => "GIF";
@@ -32,9 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
public IEnumerable FileExtensions => GifConstants.FileExtensions;
///
- public GifMetadata CreateDefaultFormatMetadata() => new GifMetadata();
+ public GifMetadata CreateDefaultFormatMetadata() => new();
///
- public GifFrameMetadata CreateDefaultFormatFrameMetadata() => new GifFrameMetadata();
+ public GifFrameMetadata CreateDefaultFormatFrameMetadata() => new();
}
}
diff --git a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs
index c254a7650..2ebf73d7e 100644
--- a/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs
+++ b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Gif
{
diff --git a/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs b/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs
index 736b9246d..4f006b799 100644
--- a/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs
+++ b/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/Gif/GifMetadata.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs
index 686288a98..de09347be 100644
--- a/src/ImageSharp/Formats/Gif/GifMetadata.cs
+++ b/src/ImageSharp/Formats/Gif/GifMetadata.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
diff --git a/src/ImageSharp/Formats/Gif/GifThrowHelper.cs b/src/ImageSharp/Formats/Gif/GifThrowHelper.cs
index b85bb139a..88fb26492 100644
--- a/src/ImageSharp/Formats/Gif/GifThrowHelper.cs
+++ b/src/ImageSharp/Formats/Gif/GifThrowHelper.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs
index 56bb6d651..74068e162 100644
--- a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs
+++ b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Metadata;
diff --git a/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
index 909d1c3a7..dfc1e69c7 100644
--- a/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
using SixLabors.ImageSharp.Memory;
diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs
index 68227db53..893fde55e 100644
--- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
@@ -64,21 +64,30 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Decodes and decompresses all pixel indices from the stream.
///
- /// Size of the data.
+ /// Minimum code size of the data.
/// The pixel array to decode to.
- public void DecodePixels(int dataSize, Buffer2D pixels)
+ public void DecodePixels(int minCodeSize, Buffer2D pixels)
{
- Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize));
+ // Calculate the clear code. The value of the clear code is 2 ^ minCodeSize
+ int clearCode = 1 << minCodeSize;
+
+ // It is possible to specify a larger LZW minimum code size than the palette length in bits
+ // which may leave a gap in the codes where no colors are assigned.
+ // http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression
+ if (minCodeSize < 2 || clearCode > MaxStackSize)
+ {
+ // Don't attempt to decode the frame indices.
+ // Theoretically we could determine a min code size from the length of the provided
+ // color palette but we won't bother since the image is most likely corrupted.
+ GifThrowHelper.ThrowInvalidImageContentException("Gif Image does not contain a valid LZW minimum code.");
+ }
// The resulting index table length.
int width = pixels.Width;
int height = pixels.Height;
int length = width * height;
- // Calculate the clear code. The value of the clear code is 2 ^ dataSize
- int clearCode = 1 << dataSize;
-
- int codeSize = dataSize + 1;
+ int codeSize = minCodeSize + 1;
// Calculate the end code
int endCode = clearCode + 1;
@@ -165,7 +174,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (code == clearCode)
{
// Reset the decoder
- codeSize = dataSize + 1;
+ codeSize = minCodeSize + 1;
codeMask = (1 << codeSize) - 1;
availableCode = clearCode + 2;
oldCode = NullCode;
diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
index c52e34f96..1146bcf91 100644
--- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs
index 2e8dda5c0..64bce03a2 100644
--- a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Metadata;
diff --git a/src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs b/src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs
index 801849c9b..7dcf38f58 100644
--- a/src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
@@ -71,13 +71,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
dest = this;
- return 5;
+ return ((IGifExtension)this).ContentLength;
}
public static GifGraphicControlExtension Parse(ReadOnlySpan buffer)
- {
- return MemoryMarshal.Cast(buffer)[0];
- }
+ => MemoryMarshal.Cast(buffer)[0];
public static byte GetPackedValue(GifDisposalMethod disposalMethod, bool userInputFlag = false, bool transparencyFlag = false)
{
diff --git a/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs b/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs
index 1eaebe11d..07e0f6f0c 100644
--- a/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
index e3bc2e883..4b2dad895 100644
--- a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs b/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs
index 2c7bed611..d9ee93883 100644
--- a/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
@@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
// 0 means loop indefinitely. Count is set as play n + 1 times.
BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(14, 2), this.RepeatCount);
- return 16; // Length - Introducer + Label + Terminator.
+ return this.ContentLength; // Length - Introducer + Label + Terminator.
}
}
}
diff --git a/src/ImageSharp/Formats/Gif/Sections/GifXmpApplicationExtension.cs b/src/ImageSharp/Formats/Gif/Sections/GifXmpApplicationExtension.cs
index 236508fe9..d3a621e7b 100644
--- a/src/ImageSharp/Formats/Gif/Sections/GifXmpApplicationExtension.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/GifXmpApplicationExtension.cs
@@ -1,10 +1,10 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
-using System.Collections.Generic;
using System.IO;
-using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
+using SixLabors.ImageSharp.IO;
+using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Gif
{
@@ -14,7 +14,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
public byte Label => GifConstants.ApplicationExtensionLabel;
- public int ContentLength => this.Data.Length + 269; // 12 + Data Length + 1 + 256
+ // size : 1
+ // identifier : 11
+ // magic trailer : 257
+ public int ContentLength => (this.Data.Length > 0) ? this.Data.Length + 269 : 0;
///
/// Gets the raw Data.
@@ -25,51 +28,28 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Reads the XMP metadata from the specified stream.
///
/// The stream to read from.
+ /// The memory allocator.
/// The XMP metadata
/// Thrown if the XMP block is not properly terminated.
- public static GifXmpApplicationExtension Read(Stream stream)
+ public static GifXmpApplicationExtension Read(Stream stream, MemoryAllocator allocator)
{
- // Read data in blocks, until an \0 character is encountered.
- // We overshoot, indicated by the terminatorIndex variable.
- const int bufferSize = 256;
- var list = new List();
- int terminationIndex = -1;
- while (terminationIndex < 0)
- {
- byte[] temp = new byte[bufferSize];
- int bytesRead = stream.Read(temp);
- list.Add(temp);
- terminationIndex = Array.IndexOf(temp, (byte)1);
- }
+ byte[] xmpBytes = ReadXmpData(stream, allocator);
- // Pack all the blocks (except magic trailer) into one single array again.
- int dataSize = ((list.Count - 1) * bufferSize) + terminationIndex;
- byte[] buffer = new byte[dataSize];
- Span bufferSpan = buffer;
- int pos = 0;
- for (int j = 0; j < list.Count - 1; j++)
+ // Exclude the "magic trailer", see XMP Specification Part 3, 1.1.2 GIF
+ int xmpLength = xmpBytes.Length - 256; // 257 - unread 0x0
+ byte[] buffer = Array.Empty();
+ if (xmpLength > 0)
{
- list[j].CopyTo(bufferSpan.Slice(pos));
- pos += bufferSize;
+ buffer = new byte[xmpLength];
+ xmpBytes.AsSpan(0, xmpLength).CopyTo(buffer);
+ stream.Skip(1); // Skip the terminator.
}
- // Last one only needs the portion until terminationIndex copied over.
- Span lastBytes = list[list.Count - 1];
- lastBytes.Slice(0, terminationIndex).CopyTo(bufferSpan.Slice(pos));
-
- // Skip the remainder of the magic trailer.
- stream.Skip(258 - (bufferSize - terminationIndex));
return new GifXmpApplicationExtension(buffer);
}
public int WriteTo(Span buffer)
{
- int totalSize = this.ContentLength;
- if (buffer.Length < totalSize)
- {
- throw new InsufficientMemoryException("Unable to write XMP metadata to GIF image");
- }
-
int bytesWritten = 0;
buffer[bytesWritten++] = GifConstants.ApplicationBlockSize;
@@ -91,7 +71,28 @@ namespace SixLabors.ImageSharp.Formats.Gif
buffer[bytesWritten++] = 0x00;
- return totalSize;
+ return this.ContentLength;
+ }
+
+ private static byte[] ReadXmpData(Stream stream, MemoryAllocator allocator)
+ {
+ using ChunkedMemoryStream bytes = new(allocator);
+
+ // XMP data doesn't have a fixed length nor is there an indicator of the length.
+ // So we simply read one byte at a time until we hit the 0x0 value at the end
+ // of the magic trailer or the end of the stream.
+ // Using ChunkedMemoryStream reduces the array resize allocation normally associated
+ // with writing from a non fixed-size buffer.
+ while (true)
+ {
+ int b = stream.ReadByte();
+ if (b <= 0)
+ {
+ return bytes.ToArray();
+ }
+
+ bytes.WriteByte((byte)b);
+ }
}
}
}
diff --git a/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs b/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
index d2783fc48..59cef0d2e 100644
--- a/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs
index b55f1119b..82c7804d5 100644
--- a/src/ImageSharp/Formats/IImageDecoder.cs
+++ b/src/ImageSharp/Formats/IImageDecoder.cs
@@ -1,9 +1,8 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats
@@ -13,26 +12,6 @@ namespace SixLabors.ImageSharp.Formats
///
public interface IImageDecoder
{
- ///
- /// Decodes the image from the specified stream to an of a specific pixel type.
- ///
- /// The pixel format.
- /// The configuration for the image.
- /// The containing image data.
- /// The .
- // TODO: Document ImageFormatExceptions (https://github.com/SixLabors/ImageSharp/issues/1110)
- Image Decode(Configuration configuration, Stream stream)
- where TPixel : unmanaged, IPixel;
-
- ///
- /// Decodes the image from the specified stream to an .
- ///
- /// The configuration for the image.
- /// The containing image data.
- /// The .
- // TODO: Document ImageFormatExceptions (https://github.com/SixLabors/ImageSharp/issues/1110)
- Image Decode(Configuration configuration, Stream stream);
-
///
/// Decodes the image from the specified stream to an of a specific pixel type.
///
@@ -42,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats
/// The token to monitor for cancellation requests.
/// The .
// TODO: Document ImageFormatExceptions (https://github.com/SixLabors/ImageSharp/issues/1110)
- Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
+ Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel;
///
@@ -53,6 +32,6 @@ namespace SixLabors.ImageSharp.Formats
/// The token to monitor for cancellation requests.
/// The .
// TODO: Document ImageFormatExceptions (https://github.com/SixLabors/ImageSharp/issues/1110)
- Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken);
+ Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken);
}
}
diff --git a/src/ImageSharp/Formats/IImageDecoderInternals.cs b/src/ImageSharp/Formats/IImageDecoderInternals.cs
index e190f7add..db3d71b89 100644
--- a/src/ImageSharp/Formats/IImageDecoderInternals.cs
+++ b/src/ImageSharp/Formats/IImageDecoderInternals.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Threading;
diff --git a/src/ImageSharp/Formats/IImageEncoder.cs b/src/ImageSharp/Formats/IImageEncoder.cs
index e5a1b1c83..40831ed7c 100644
--- a/src/ImageSharp/Formats/IImageEncoder.cs
+++ b/src/ImageSharp/Formats/IImageEncoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
diff --git a/src/ImageSharp/Formats/IImageEncoderInternals.cs b/src/ImageSharp/Formats/IImageEncoderInternals.cs
index d44ac45f2..824a08f5a 100644
--- a/src/ImageSharp/Formats/IImageEncoderInternals.cs
+++ b/src/ImageSharp/Formats/IImageEncoderInternals.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
diff --git a/src/ImageSharp/Formats/IImageFormat.cs b/src/ImageSharp/Formats/IImageFormat.cs
index 812984ba8..c853ca413 100644
--- a/src/ImageSharp/Formats/IImageFormat.cs
+++ b/src/ImageSharp/Formats/IImageFormat.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
diff --git a/src/ImageSharp/Formats/IImageFormatDetector.cs b/src/ImageSharp/Formats/IImageFormatDetector.cs
index 96c3a49af..4175cf0ec 100644
--- a/src/ImageSharp/Formats/IImageFormatDetector.cs
+++ b/src/ImageSharp/Formats/IImageFormatDetector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/IImageInfoDetector.cs b/src/ImageSharp/Formats/IImageInfoDetector.cs
index 6f5fc2333..2d30af5ce 100644
--- a/src/ImageSharp/Formats/IImageInfoDetector.cs
+++ b/src/ImageSharp/Formats/IImageInfoDetector.cs
@@ -1,9 +1,8 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace SixLabors.ImageSharp.Formats
{
@@ -12,14 +11,6 @@ namespace SixLabors.ImageSharp.Formats
///
public interface IImageInfoDetector
{
- ///
- /// Reads the raw image information from the specified stream.
- ///
- /// The configuration for the image.
- /// The containing image data.
- /// The object
- IImageInfo Identify(Configuration configuration, Stream stream);
-
///
/// Reads the raw image information from the specified stream.
///
@@ -27,6 +18,6 @@ namespace SixLabors.ImageSharp.Formats
/// The containing image data.
/// The token to monitor for cancellation requests.
/// The object
- Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken);
+ IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken);
}
}
diff --git a/src/ImageSharp/Formats/ImageDecoderUtilities.cs b/src/ImageSharp/Formats/ImageDecoderUtilities.cs
index 49e783ee4..b6cefd2c8 100644
--- a/src/ImageSharp/Formats/ImageDecoderUtilities.cs
+++ b/src/ImageSharp/Formats/ImageDecoderUtilities.cs
@@ -1,10 +1,9 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -13,153 +12,45 @@ namespace SixLabors.ImageSharp.Formats
{
internal static class ImageDecoderUtilities
{
- ///
- /// Reads the raw image information from the specified stream.
- ///
- /// The decoder.
- /// /// The configuration for the image.
- /// The containing image data.
- /// The token to monitor for cancellation requests.
- /// is null.
- /// A representing the asynchronous operation.
- public static Task IdentifyAsync(
- this IImageDecoderInternals decoder,
- Configuration configuration,
- Stream stream,
- CancellationToken cancellationToken)
- => decoder.IdentifyAsync(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken);
-
- ///
- /// Reads the raw image information from the specified stream.
- ///
- /// The decoder.
- /// The configuration for the image.
- /// The containing image data.
- /// Factory method to handle as .
- /// The token to monitor for cancellation requests.
- /// is null.
- /// A representing the asynchronous operation.
- public static Task IdentifyAsync(
+ public static IImageInfo Identify(
this IImageDecoderInternals decoder,
Configuration configuration,
Stream stream,
- Func tooLargeImageExceptionFactory,
CancellationToken cancellationToken)
{
+ using var bufferedReadStream = new BufferedReadStream(configuration, stream);
+
try
{
- using var bufferedReadStream = new BufferedReadStream(configuration, stream);
- IImageInfo imageInfo = decoder.Identify(bufferedReadStream, cancellationToken);
- return Task.FromResult(imageInfo);
+ return decoder.Identify(bufferedReadStream, cancellationToken);
}
catch (InvalidMemoryOperationException ex)
{
- InvalidImageContentException invalidImageContentException = tooLargeImageExceptionFactory(ex, decoder.Dimensions);
- return Task.FromException(invalidImageContentException);
- }
- catch (OperationCanceledException)
- {
- return Task.FromCanceled(cancellationToken);
- }
- catch (Exception ex)
- {
- return Task.FromException(ex);
+ throw new InvalidImageContentException(decoder.Dimensions, ex);
}
}
- ///
- /// Decodes the image from the specified stream.
- ///
- /// The pixel format.
- /// The decoder.
- /// The configuration for the image.
- /// The containing image data.
- /// The token to monitor for cancellation requests.
- /// A representing the asynchronous operation.
- public static Task> DecodeAsync(
- this IImageDecoderInternals decoder,
- Configuration configuration,
- Stream stream,
- CancellationToken cancellationToken)
- where TPixel : unmanaged, IPixel =>
- decoder.DecodeAsync(
- configuration,
- stream,
- DefaultLargeImageExceptionFactory,
- cancellationToken);
-
- ///
- /// Decodes the image from the specified stream.
- ///
- /// The pixel format.
- /// The decoder.
- /// The configuration for the image.
- /// The containing image data.
- /// Factory method to handle as .
- /// The token to monitor for cancellation requests.
- /// A representing the asynchronous operation.
- public static Task> DecodeAsync(
+ public static Image Decode(
this IImageDecoderInternals decoder,
Configuration configuration,
Stream stream,
- Func largeImageExceptionFactory,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
- {
- try
- {
- using var bufferedReadStream = new BufferedReadStream(configuration, stream);
- Image image = decoder.Decode(bufferedReadStream, cancellationToken);
- return Task.FromResult(image);
- }
- catch (InvalidMemoryOperationException ex)
- {
- InvalidImageContentException invalidImageContentException = largeImageExceptionFactory(ex, decoder.Dimensions);
- return Task.FromException>(invalidImageContentException);
- }
- catch (OperationCanceledException)
- {
- return Task.FromCanceled>(cancellationToken);
- }
- catch (Exception ex)
- {
- return Task.FromException>(ex);
- }
- }
-
- public static IImageInfo Identify(
- this IImageDecoderInternals decoder,
- Configuration configuration,
- Stream stream)
- {
- using var bufferedReadStream = new BufferedReadStream(configuration, stream);
-
- try
- {
- return decoder.Identify(bufferedReadStream, default);
- }
- catch (InvalidMemoryOperationException ex)
- {
- throw new InvalidImageContentException(decoder.Dimensions, ex);
- }
- }
-
- public static Image Decode(this IImageDecoderInternals decoder, Configuration configuration, Stream stream)
- where TPixel : unmanaged, IPixel
- => decoder.Decode(configuration, stream, DefaultLargeImageExceptionFactory);
+ => decoder.Decode(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken);
public static Image Decode(
this IImageDecoderInternals decoder,
Configuration configuration,
Stream stream,
- Func largeImageExceptionFactory)
+ Func largeImageExceptionFactory,
+ CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
using var bufferedReadStream = new BufferedReadStream(configuration, stream);
try
{
- return decoder.Decode(bufferedReadStream, default);
+ return decoder.Decode(bufferedReadStream, cancellationToken);
}
catch (InvalidMemoryOperationException ex)
{
diff --git a/src/ImageSharp/Formats/ImageEncoderUtilities.cs b/src/ImageSharp/Formats/ImageEncoderUtilities.cs
index 896fffa6f..d9fb70150 100644
--- a/src/ImageSharp/Formats/ImageEncoderUtilities.cs
+++ b/src/ImageSharp/Formats/ImageEncoderUtilities.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.IO;
diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.cs b/src/ImageSharp/Formats/ImageExtensions.Save.cs
index b6321ddbf..4938675ca 100644
--- a/src/ImageSharp/Formats/ImageExtensions.Save.cs
+++ b/src/ImageSharp/Formats/ImageExtensions.Save.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
//
using System.IO;
diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.tt b/src/ImageSharp/Formats/ImageExtensions.Save.tt
index ae7648522..2a756b483 100644
--- a/src/ImageSharp/Formats/ImageExtensions.Save.tt
+++ b/src/ImageSharp/Formats/ImageExtensions.Save.tt
@@ -2,7 +2,7 @@
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
//
using System.IO;
diff --git a/src/ImageSharp/Formats/ImageFormatManager.cs b/src/ImageSharp/Formats/ImageFormatManager.cs
index f3fde403d..9f3f0d47f 100644
--- a/src/ImageSharp/Formats/ImageFormatManager.cs
+++ b/src/ImageSharp/Formats/ImageFormatManager.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Collections.Concurrent;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs
index 002d382dc..8eba118fd 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.InteropServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
index 4b03f9f7b..d83edbd5b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
index dd5d3f196..10501bada 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
index 8897efbe0..7d5a347a1 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
@@ -1,6 +1,6 @@
<#
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#>
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
@@ -9,7 +9,7 @@
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs
index 0971ccdca..2a03f911b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System;
@@ -35,8 +35,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[FieldOffset(224)]
public Vector256 V7;
- private static readonly Vector256 MultiplyIntoInt16ShuffleMask = Vector256.Create(0, 1, 4, 5, 2, 3, 6, 7);
-
private static unsafe void MultiplyIntoInt16_Avx2(ref Block8x8F a, ref Block8x8F b, ref Block8x8 dest)
{
DebugGuard.IsTrue(Avx2.IsSupported, "Avx2 support is required to run this operation!");
@@ -45,6 +43,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
ref Vector256 bBase = ref b.V0;
ref Vector256 destRef = ref dest.V01;
+ Vector256 multiplyIntoInt16ShuffleMask = Vector256.Create(0, 1, 4, 5, 2, 3, 6, 7);
for (nint i = 0; i < 8; i += 2)
{
@@ -52,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
Vector256 row1 = Avx.ConvertToVector256Int32(Avx.Multiply(Unsafe.Add(ref aBase, i + 1), Unsafe.Add(ref bBase, i + 1)));
Vector256 row = Avx2.PackSignedSaturate(row0, row1);
- row = Avx2.PermuteVar8x32(row.AsInt32(), MultiplyIntoInt16ShuffleMask).AsInt16();
+ row = Avx2.PermuteVar8x32(row.AsInt32(), multiplyIntoInt16ShuffleMask).AsInt16();
Unsafe.Add(ref destRef, (IntPtr)((uint)i / 2)) = row;
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopyTo.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopyTo.cs
index 498fe4d03..b9fb18b8b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopyTo.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopyTo.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index d7511fdda..6faaadac7 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ComponentType.cs b/src/ImageSharp/Formats/Jpeg/Components/ComponentType.cs
new file mode 100644
index 000000000..0c0b0e792
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/ComponentType.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
+{
+ internal enum ComponentType
+ {
+ Huffman = 0,
+
+ Arithmetic = 1
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs
index b41d52aa4..acf43092c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticDecodingComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticDecodingComponent.cs
new file mode 100644
index 000000000..043df5706
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticDecodingComponent.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
+{
+ internal class ArithmeticDecodingComponent : JpegComponent
+ {
+ public ArithmeticDecodingComponent(MemoryAllocator memoryAllocator, JpegFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index)
+ : base(memoryAllocator, frame, id, horizontalFactor, verticalFactor, quantizationTableIndex, index)
+ {
+ }
+
+ ///
+ /// Gets or sets the dc context.
+ ///
+ public int DcContext { get; set; }
+
+ ///
+ /// Gets or sets the dc statistics.
+ ///
+ public ArithmeticStatistics DcStatistics { get; set; }
+
+ ///
+ /// Gets or sets the ac statistics.
+ ///
+ public ArithmeticStatistics AcStatistics { get; set; }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticDecodingTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticDecodingTable.cs
new file mode 100644
index 000000000..4c02852d5
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticDecodingTable.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
+{
+ internal class ArithmeticDecodingTable
+ {
+ public ArithmeticDecodingTable(byte tableClass, byte identifier)
+ {
+ this.TableClass = tableClass;
+ this.Identifier = identifier;
+ }
+
+ public byte TableClass { get; }
+
+ public byte Identifier { get; }
+
+ public byte ConditioningTableValue { get; private set; }
+
+ public int DcL { get; private set; }
+
+ public int DcU { get; private set; }
+
+ public int AcKx { get; private set; }
+
+ public void Configure(byte conditioningTableValue)
+ {
+ this.ConditioningTableValue = conditioningTableValue;
+ if (this.TableClass == 0)
+ {
+ this.DcL = conditioningTableValue & 0x0F;
+ this.DcU = conditioningTableValue >> 4;
+ this.AcKx = 0;
+ }
+ else
+ {
+ this.DcL = 0;
+ this.DcU = 0;
+ this.AcKx = conditioningTableValue;
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticScanDecoder.cs
new file mode 100644
index 000000000..ae97c7e54
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticScanDecoder.cs
@@ -0,0 +1,1239 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using SixLabors.ImageSharp.IO;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
+{
+ ///
+ /// Decodes a arithmetic encoded spectral scan.
+ /// Based on https://github.com/yigolden/JpegLibrary/blob/main/src/JpegLibrary/ScanDecoder/JpegArithmeticScanDecoder.cs
+ ///
+ internal class ArithmeticScanDecoder : IJpegScanDecoder
+ {
+ private readonly BufferedReadStream stream;
+
+ private int c;
+ private int a;
+ private int ct;
+
+ ///
+ /// instance containing decoding-related information.
+ ///
+ private JpegFrame frame;
+
+ ///
+ /// Shortcut for .Components.
+ ///
+ private IJpegComponent[] components;
+
+ ///
+ /// Number of component in the current scan.
+ ///
+ private int scanComponentCount;
+
+ ///
+ /// The reset interval determined by RST markers.
+ ///
+ private int restartInterval;
+
+ ///
+ /// How many mcu's are left to do.
+ ///
+ private int todo;
+
+ private readonly SpectralConverter spectralConverter;
+
+ private JpegBitReader scanBuffer;
+
+ private ArithmeticDecodingTable[] dcDecodingTables;
+
+ private ArithmeticDecodingTable[] acDecodingTables;
+
+ private readonly byte[] fixedBin = { 113, 0, 0, 0 };
+
+ private readonly CancellationToken cancellationToken;
+
+ private static readonly int[] ArithmeticTable =
+ {
+ Pack(0x5a1d, 1, 1, 1),
+ Pack(0x2586, 14, 2, 0),
+ Pack(0x1114, 16, 3, 0),
+ Pack(0x080b, 18, 4, 0),
+ Pack(0x03d8, 20, 5, 0),
+ Pack(0x01da, 23, 6, 0),
+ Pack(0x00e5, 25, 7, 0),
+ Pack(0x006f, 28, 8, 0),
+ Pack(0x0036, 30, 9, 0),
+ Pack(0x001a, 33, 10, 0),
+ Pack(0x000d, 35, 11, 0),
+ Pack(0x0006, 9, 12, 0),
+ Pack(0x0003, 10, 13, 0),
+ Pack(0x0001, 12, 13, 0),
+ Pack(0x5a7f, 15, 15, 1),
+ Pack(0x3f25, 36, 16, 0),
+ Pack(0x2cf2, 38, 17, 0),
+ Pack(0x207c, 39, 18, 0),
+ Pack(0x17b9, 40, 19, 0),
+ Pack(0x1182, 42, 20, 0),
+ Pack(0x0cef, 43, 21, 0),
+ Pack(0x09a1, 45, 22, 0),
+ Pack(0x072f, 46, 23, 0),
+ Pack(0x055c, 48, 24, 0),
+ Pack(0x0406, 49, 25, 0),
+ Pack(0x0303, 51, 26, 0),
+ Pack(0x0240, 52, 27, 0),
+ Pack(0x01b1, 54, 28, 0),
+ Pack(0x0144, 56, 29, 0),
+ Pack(0x00f5, 57, 30, 0),
+ Pack(0x00b7, 59, 31, 0),
+ Pack(0x008a, 60, 32, 0),
+ Pack(0x0068, 62, 33, 0),
+ Pack(0x004e, 63, 34, 0),
+ Pack(0x003b, 32, 35, 0),
+ Pack(0x002c, 33, 9, 0),
+ Pack(0x5ae1, 37, 37, 1),
+ Pack(0x484c, 64, 38, 0),
+ Pack(0x3a0d, 65, 39, 0),
+ Pack(0x2ef1, 67, 40, 0),
+ Pack(0x261f, 68, 41, 0),
+ Pack(0x1f33, 69, 42, 0),
+ Pack(0x19a8, 70, 43, 0),
+ Pack(0x1518, 72, 44, 0),
+ Pack(0x1177, 73, 45, 0),
+ Pack(0x0e74, 74, 46, 0),
+ Pack(0x0bfb, 75, 47, 0),
+ Pack(0x09f8, 77, 48, 0),
+ Pack(0x0861, 78, 49, 0),
+ Pack(0x0706, 79, 50, 0),
+ Pack(0x05cd, 48, 51, 0),
+ Pack(0x04de, 50, 52, 0),
+ Pack(0x040f, 50, 53, 0),
+ Pack(0x0363, 51, 54, 0),
+ Pack(0x02d4, 52, 55, 0),
+ Pack(0x025c, 53, 56, 0),
+ Pack(0x01f8, 54, 57, 0),
+ Pack(0x01a4, 55, 58, 0),
+ Pack(0x0160, 56, 59, 0),
+ Pack(0x0125, 57, 60, 0),
+ Pack(0x00f6, 58, 61, 0),
+ Pack(0x00cb, 59, 62, 0),
+ Pack(0x00ab, 61, 63, 0),
+ Pack(0x008f, 61, 32, 0),
+ Pack(0x5b12, 65, 65, 1),
+ Pack(0x4d04, 80, 66, 0),
+ Pack(0x412c, 81, 67, 0),
+ Pack(0x37d8, 82, 68, 0),
+ Pack(0x2fe8, 83, 69, 0),
+ Pack(0x293c, 84, 70, 0),
+ Pack(0x2379, 86, 71, 0),
+ Pack(0x1edf, 87, 72, 0),
+ Pack(0x1aa9, 87, 73, 0),
+ Pack(0x174e, 72, 74, 0),
+ Pack(0x1424, 72, 75, 0),
+ Pack(0x119c, 74, 76, 0),
+ Pack(0x0f6b, 74, 77, 0),
+ Pack(0x0d51, 75, 78, 0),
+ Pack(0x0bb6, 77, 79, 0),
+ Pack(0x0a40, 77, 48, 0),
+ Pack(0x5832, 80, 81, 1),
+ Pack(0x4d1c, 88, 82, 0),
+ Pack(0x438e, 89, 83, 0),
+ Pack(0x3bdd, 90, 84, 0),
+ Pack(0x34ee, 91, 85, 0),
+ Pack(0x2eae, 92, 86, 0),
+ Pack(0x299a, 93, 87, 0),
+ Pack(0x2516, 86, 71, 0),
+ Pack(0x5570, 88, 89, 1),
+ Pack(0x4ca9, 95, 90, 0),
+ Pack(0x44d9, 96, 91, 0),
+ Pack(0x3e22, 97, 92, 0),
+ Pack(0x3824, 99, 93, 0),
+ Pack(0x32b4, 99, 94, 0),
+ Pack(0x2e17, 93, 86, 0),
+ Pack(0x56a8, 95, 96, 1),
+ Pack(0x4f46, 101, 97, 0),
+ Pack(0x47e5, 102, 98, 0),
+ Pack(0x41cf, 103, 99, 0),
+ Pack(0x3c3d, 104, 100, 0),
+ Pack(0x375e, 99, 93, 0),
+ Pack(0x5231, 105, 102, 0),
+ Pack(0x4c0f, 106, 103, 0),
+ Pack(0x4639, 107, 104, 0),
+ Pack(0x415e, 103, 99, 0),
+ Pack(0x5627, 105, 106, 1),
+ Pack(0x50e7, 108, 107, 0),
+ Pack(0x4b85, 109, 103, 0),
+ Pack(0x5597, 110, 109, 0),
+ Pack(0x504f, 111, 107, 0),
+ Pack(0x5a10, 110, 111, 1),
+ Pack(0x5522, 112, 109, 0),
+ Pack(0x59eb, 112, 111, 1),
+
+ // This last entry is used for fixed probability estimate of 0.5
+ // as suggested in Section 10.3 Table 5 of ITU-T Rec. T.851.
+ Pack(0x5a1d, 113, 113, 0)
+ };
+
+ private readonly List statistics = new();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input stream.
+ /// Spectral to pixel converter.
+ /// The token to monitor cancellation.
+ public ArithmeticScanDecoder(BufferedReadStream stream, SpectralConverter converter, CancellationToken cancellationToken)
+ {
+ this.stream = stream;
+ this.spectralConverter = converter;
+ this.cancellationToken = cancellationToken;
+
+ this.c = 0;
+ this.a = 0;
+ this.ct = -16; // Force reading 2 initial bytes to fill C.
+ }
+
+ ///
+ public int ResetInterval
+ {
+ set
+ {
+ this.restartInterval = value;
+ this.todo = value;
+ }
+ }
+
+ ///
+ public int SpectralStart { get; set; }
+
+ ///
+ public int SpectralEnd { get; set; }
+
+ ///
+ public int SuccessiveHigh { get; set; }
+
+ ///
+ public int SuccessiveLow { get; set; }
+
+ public void InitDecodingTables(List arithmeticDecodingTables)
+ {
+ for (int i = 0; i < this.components.Length; i++)
+ {
+ var component = this.components[i] as ArithmeticDecodingComponent;
+ this.dcDecodingTables[i] = this.GetArithmeticTable(arithmeticDecodingTables, true, component.DcTableId);
+ component.DcStatistics = this.CreateOrGetStatisticsBin(true, component.DcTableId);
+ this.acDecodingTables[i] = this.GetArithmeticTable(arithmeticDecodingTables, false, component.AcTableId);
+ component.AcStatistics = this.CreateOrGetStatisticsBin(false, component.AcTableId);
+ }
+ }
+
+ private ref byte GetFixedBinReference() => ref this.fixedBin[0];
+
+ ///
+ /// Decodes the entropy coded data.
+ ///
+ /// Component count in the current scan.
+ public void ParseEntropyCodedData(int scanComponentCount)
+ {
+ this.cancellationToken.ThrowIfCancellationRequested();
+
+ this.scanComponentCount = scanComponentCount;
+
+ this.scanBuffer = new JpegBitReader(this.stream);
+
+ this.frame.AllocateComponents();
+
+ if (this.frame.Progressive)
+ {
+ this.ParseProgressiveData();
+ }
+ else
+ {
+ this.ParseBaselineData();
+ }
+
+ if (this.scanBuffer.HasBadMarker())
+ {
+ this.stream.Position = this.scanBuffer.MarkerPosition;
+ }
+ }
+
+ ///
+ public void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
+ {
+ this.frame = frame;
+ this.components = frame.Components;
+
+ this.dcDecodingTables = new ArithmeticDecodingTable[this.components.Length];
+ this.acDecodingTables = new ArithmeticDecodingTable[this.components.Length];
+
+ this.spectralConverter.InjectFrameData(frame, jpegData);
+ }
+
+ private ArithmeticDecodingTable GetArithmeticTable(List arithmeticDecodingTables, bool isDcTable, int identifier)
+ {
+ int tableClass = isDcTable ? 0 : 1;
+
+ foreach (ArithmeticDecodingTable item in arithmeticDecodingTables)
+ {
+ if (item.TableClass == tableClass && item.Identifier == identifier)
+ {
+ return item;
+ }
+ }
+
+ return null;
+ }
+
+ private ArithmeticStatistics CreateOrGetStatisticsBin(bool dc, int identifier, bool reset = false)
+ {
+ foreach (ArithmeticStatistics item in this.statistics)
+ {
+ if (item.IsDcStatistics == dc && item.Identifier == identifier)
+ {
+ if (reset)
+ {
+ item.Reset();
+ }
+
+ return item;
+ }
+ }
+
+ var statistic = new ArithmeticStatistics(dc, identifier);
+ this.statistics.Add(statistic);
+ return statistic;
+ }
+
+ private void ParseBaselineData()
+ {
+ foreach (ArithmeticDecodingComponent component in this.components)
+ {
+ component.DcPredictor = 0;
+ component.DcContext = 0;
+ component.DcStatistics?.Reset();
+ component.AcStatistics?.Reset();
+ }
+
+ this.Reset();
+
+ if (this.scanComponentCount != 1)
+ {
+ this.spectralConverter.PrepareForDecoding();
+ this.ParseBaselineDataInterleaved();
+ this.spectralConverter.CommitConversion();
+ }
+ else if (this.frame.ComponentCount == 1)
+ {
+ this.spectralConverter.PrepareForDecoding();
+ this.ParseBaselineDataSingleComponent();
+ this.spectralConverter.CommitConversion();
+ }
+ else
+ {
+ this.ParseBaselineDataNonInterleaved();
+ }
+ }
+
+ private void ParseProgressiveData()
+ {
+ this.CheckProgressiveData();
+
+ foreach (ArithmeticDecodingComponent component in this.components)
+ {
+ if (this.SpectralStart == 0 && this.SuccessiveHigh == 0)
+ {
+ component.DcPredictor = 0;
+ component.DcContext = 0;
+ component.DcStatistics?.Reset();
+ }
+
+ if (this.SpectralStart != 0)
+ {
+ component.AcStatistics?.Reset();
+ }
+ }
+
+ this.Reset();
+
+ if (this.scanComponentCount == 1)
+ {
+ this.ParseProgressiveDataNonInterleaved();
+ }
+ else
+ {
+ this.ParseProgressiveDataInterleaved();
+ }
+ }
+
+ private void CheckProgressiveData()
+ {
+ // Validate successive scan parameters.
+ // Logic has been adapted from libjpeg.
+ // See Table B.3 – Scan header parameter size and values. itu-t81.pdf
+ bool invalid = false;
+ if (this.SpectralStart == 0)
+ {
+ if (this.SpectralEnd != 0)
+ {
+ invalid = true;
+ }
+ }
+ else
+ {
+ // Need not check Ss/Se < 0 since they came from unsigned bytes.
+ if (this.SpectralEnd < this.SpectralStart || this.SpectralEnd > 63)
+ {
+ invalid = true;
+ }
+
+ // AC scans may have only one component.
+ if (this.scanComponentCount != 1)
+ {
+ invalid = true;
+ }
+ }
+
+ if (this.SuccessiveHigh != 0)
+ {
+ // Successive approximation refinement scan: must have Al = Ah-1.
+ if (this.SuccessiveHigh - 1 != this.SuccessiveLow)
+ {
+ invalid = true;
+ }
+ }
+
+ // TODO: How does this affect 12bit jpegs.
+ // According to libjpeg the range covers 8bit only?
+ if (this.SuccessiveLow > 13)
+ {
+ invalid = true;
+ }
+
+ if (invalid)
+ {
+ JpegThrowHelper.ThrowBadProgressiveScan(this.SpectralStart, this.SpectralEnd, this.SuccessiveHigh, this.SuccessiveLow);
+ }
+ }
+
+ private void ParseBaselineDataInterleaved()
+ {
+ int mcu = 0;
+ int mcusPerColumn = this.frame.McusPerColumn;
+ int mcusPerLine = this.frame.McusPerLine;
+ ref JpegBitReader reader = ref this.scanBuffer;
+
+ for (int j = 0; j < mcusPerColumn; j++)
+ {
+ this.cancellationToken.ThrowIfCancellationRequested();
+
+ // Decode from binary to spectral.
+ for (int i = 0; i < mcusPerLine; i++)
+ {
+ // Scan an interleaved mcu... process components in order.
+ int mcuCol = mcu % mcusPerLine;
+ for (int k = 0; k < this.scanComponentCount; k++)
+ {
+ int order = this.frame.ComponentOrder[k];
+ var component = this.components[order] as ArithmeticDecodingComponent;
+
+ ref ArithmeticDecodingTable dcDecodingTable = ref this.dcDecodingTables[component.DcTableId];
+ ref ArithmeticDecodingTable acDecodingTable = ref this.acDecodingTables[component.AcTableId];
+
+ int h = component.HorizontalSamplingFactor;
+ int v = component.VerticalSamplingFactor;
+
+ // Scan out an mcu's worth of this component; that's just determined
+ // by the basic H and V specified for the component.
+ int mcuColMulh = mcuCol * h;
+ for (int y = 0; y < v; y++)
+ {
+ Span blockSpan = component.SpectralBlocks.DangerousGetRowSpan(y);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
+
+ for (int x = 0; x < h; x++)
+ {
+ if (reader.NoData)
+ {
+ // It is very likely that some spectral data was decoded before we've encountered 'end of scan'
+ // so we need to decode what's left and return (or maybe throw?)
+ this.spectralConverter.ConvertStrideBaseline();
+ return;
+ }
+
+ int blockCol = mcuColMulh + x;
+
+ this.DecodeBlockBaseline(
+ component,
+ ref Unsafe.Add(ref blockRef, (nint)(uint)blockCol),
+ ref acDecodingTable,
+ ref dcDecodingTable);
+ }
+ }
+ }
+
+ // After all interleaved components, that's an interleaved MCU,
+ // so now count down the restart interval.
+ mcu++;
+ this.HandleRestart();
+ }
+
+ // Convert from spectral to actual pixels via given converter.
+ this.spectralConverter.ConvertStrideBaseline();
+ }
+ }
+
+ private void ParseBaselineDataSingleComponent()
+ {
+ var component = this.frame.Components[0] as ArithmeticDecodingComponent;
+ int mcuLines = this.frame.McusPerColumn;
+ int w = component.WidthInBlocks;
+ int h = component.SamplingFactors.Height;
+ ref ArithmeticDecodingTable dcDecodingTable = ref this.dcDecodingTables[component.DcTableId];
+ ref ArithmeticDecodingTable acDecodingTable = ref this.acDecodingTables[component.AcTableId];
+
+ ref JpegBitReader reader = ref this.scanBuffer;
+
+ for (int i = 0; i < mcuLines; i++)
+ {
+ this.cancellationToken.ThrowIfCancellationRequested();
+
+ // Decode from binary to spectral.
+ for (int j = 0; j < h; j++)
+ {
+ Span blockSpan = component.SpectralBlocks.DangerousGetRowSpan(j);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
+
+ for (int k = 0; k < w; k++)
+ {
+ if (reader.NoData)
+ {
+ // It is very likely that some spectral data was decoded before we've encountered 'end of scan'
+ // so we need to decode what's left and return (or maybe throw?)
+ this.spectralConverter.ConvertStrideBaseline();
+ return;
+ }
+
+ this.DecodeBlockBaseline(
+ component,
+ ref Unsafe.Add(ref blockRef, (nint)(uint)k),
+ ref acDecodingTable,
+ ref dcDecodingTable);
+
+ this.HandleRestart();
+ }
+ }
+
+ // Convert from spectral to actual pixels via given converter.
+ this.spectralConverter.ConvertStrideBaseline();
+ }
+ }
+
+ private void ParseBaselineDataNonInterleaved()
+ {
+ var component = (ArithmeticDecodingComponent)this.components[this.frame.ComponentOrder[0]];
+ ref JpegBitReader reader = ref this.scanBuffer;
+
+ int w = component.WidthInBlocks;
+ int h = component.HeightInBlocks;
+
+ ref ArithmeticDecodingTable dcDecodingTable = ref this.dcDecodingTables[component.DcTableId];
+ ref ArithmeticDecodingTable acDecodingTable = ref this.acDecodingTables[component.AcTableId];
+
+ for (int j = 0; j < h; j++)
+ {
+ this.cancellationToken.ThrowIfCancellationRequested();
+ Span blockSpan = component.SpectralBlocks.DangerousGetRowSpan(j);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
+
+ for (int i = 0; i < w; i++)
+ {
+ if (reader.NoData)
+ {
+ return;
+ }
+
+ this.DecodeBlockBaseline(
+ component,
+ ref Unsafe.Add(ref blockRef, (nint)(uint)i),
+ ref acDecodingTable,
+ ref dcDecodingTable);
+
+ this.HandleRestart();
+ }
+ }
+ }
+
+ private void ParseProgressiveDataInterleaved()
+ {
+ int mcu = 0;
+ int mcusPerColumn = this.frame.McusPerColumn;
+ int mcusPerLine = this.frame.McusPerLine;
+ ref JpegBitReader reader = ref this.scanBuffer;
+
+ for (int j = 0; j < mcusPerColumn; j++)
+ {
+ for (int i = 0; i < mcusPerLine; i++)
+ {
+ // Scan an interleaved mcu... process components in order.
+ int mcuRow = Math.DivRem(mcu, mcusPerLine, out int mcuCol);
+ for (int k = 0; k < this.scanComponentCount; k++)
+ {
+ int order = this.frame.ComponentOrder[k];
+ var component = this.components[order] as ArithmeticDecodingComponent;
+ ref ArithmeticDecodingTable dcDecodingTable = ref this.dcDecodingTables[component.DcTableId];
+
+ int h = component.HorizontalSamplingFactor;
+ int v = component.VerticalSamplingFactor;
+
+ // Scan out an mcu's worth of this component; that's just determined
+ // by the basic H and V specified for the component.
+ int mcuColMulh = mcuCol * h;
+ for (int y = 0; y < v; y++)
+ {
+ int blockRow = (mcuRow * v) + y;
+ Span blockSpan = component.SpectralBlocks.DangerousGetRowSpan(blockRow);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
+
+ for (int x = 0; x < h; x++)
+ {
+ if (reader.NoData)
+ {
+ return;
+ }
+
+ int blockCol = mcuColMulh + x;
+
+ this.DecodeBlockProgressiveDc(
+ component,
+ ref Unsafe.Add(ref blockRef, (nint)(uint)blockCol),
+ ref dcDecodingTable);
+ }
+ }
+ }
+
+ // After all interleaved components, that's an interleaved MCU,
+ // so now count down the restart interval.
+ mcu++;
+ this.HandleRestart();
+ }
+ }
+ }
+
+ private void ParseProgressiveDataNonInterleaved()
+ {
+ var component = this.components[this.frame.ComponentOrder[0]] as ArithmeticDecodingComponent;
+ ref JpegBitReader reader = ref this.scanBuffer;
+
+ int w = component.WidthInBlocks;
+ int h = component.HeightInBlocks;
+
+ if (this.SpectralStart == 0)
+ {
+ ref ArithmeticDecodingTable dcDecodingTable = ref this.dcDecodingTables[component.DcTableId];
+
+ for (int j = 0; j < h; j++)
+ {
+ this.cancellationToken.ThrowIfCancellationRequested();
+
+ Span blockSpan = component.SpectralBlocks.DangerousGetRowSpan(j);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
+
+ for (int i = 0; i < w; i++)
+ {
+ if (reader.NoData)
+ {
+ return;
+ }
+
+ this.DecodeBlockProgressiveDc(
+ component,
+ ref Unsafe.Add(ref blockRef, (nint)(uint)i),
+ ref dcDecodingTable);
+
+ this.HandleRestart();
+ }
+ }
+ }
+ else
+ {
+ ref ArithmeticDecodingTable acDecodingTable = ref this.acDecodingTables[component.AcTableId];
+
+ for (int j = 0; j < h; j++)
+ {
+ this.cancellationToken.ThrowIfCancellationRequested();
+
+ Span blockSpan = component.SpectralBlocks.DangerousGetRowSpan(j);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
+
+ for (int i = 0; i < w; i++)
+ {
+ if (reader.NoData)
+ {
+ return;
+ }
+
+ this.DecodeBlockProgressiveAc(
+ component,
+ ref Unsafe.Add(ref blockRef, (nint)(uint)i),
+ ref acDecodingTable);
+
+ this.HandleRestart();
+ }
+ }
+ }
+ }
+
+ private void DecodeBlockProgressiveDc(ArithmeticDecodingComponent component, ref Block8x8 block, ref ArithmeticDecodingTable dcTable)
+ {
+ if (dcTable == null)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("DC table is missing");
+ }
+
+ ref JpegBitReader reader = ref this.scanBuffer;
+ ref short blockDataRef = ref Unsafe.As(ref block);
+
+ if (this.SuccessiveHigh == 0)
+ {
+ // First scan
+ // Sections F.2.4.1 & F.1.4.4.1: Decoding of DC coefficients.
+
+ // Table F.4: Point to statistics bin S0 for DC coefficient coding.
+ ref byte st = ref Unsafe.Add(ref component.DcStatistics.GetReference(), component.DcContext);
+
+ // Figure F.19: Decode_DC_DIFF
+ if (this.DecodeBinaryDecision(ref reader, ref st) == 0)
+ {
+ component.DcContext = 0;
+ }
+ else
+ {
+ // Figure F.21: Decoding nonzero value v.
+ // Figure F.22: Decoding the sign of v.
+ int sign = this.DecodeBinaryDecision(ref reader, ref Unsafe.Add(ref st, 1));
+ st = ref Unsafe.Add(ref st, (nint)(uint)(2 + sign));
+
+ // Figure F.23: Decoding the magnitude category of v.
+ int m = this.DecodeBinaryDecision(ref reader, ref st);
+ if (m != 0)
+ {
+ st = ref component.DcStatistics.GetReference(20);
+ while (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ if ((m <<= 1) == 0x8000)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Invalid arithmetic code.");
+ }
+
+ st = ref Unsafe.Add(ref st, 1);
+ }
+ }
+
+ // Section F.1.4.4.1.2: Establish dc_context conditioning category.
+ if (m < (int)((1L << dcTable.DcL) >> 1))
+ {
+ component.DcContext = 0; // Zero diff category.
+ }
+ else if (m > (int)((1L << dcTable.DcU) >> 1))
+ {
+ component.DcContext = 12 + (sign * 4); // Large diff category.
+ }
+ else
+ {
+ component.DcContext = 4 + (sign * 4); // Small diff category.
+ }
+
+ int v = m;
+
+ // Figure F.24: Decoding the magnitude bit pattern of v.
+ st = ref Unsafe.Add(ref st, 14);
+ while ((m >>= 1) != 0)
+ {
+ if (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ v |= m;
+ }
+ }
+
+ v += 1;
+ if (sign != 0)
+ {
+ v = -v;
+ }
+
+ component.DcPredictor = (short)(component.DcPredictor + v);
+ }
+
+ blockDataRef = (short)(component.DcPredictor << this.SuccessiveLow);
+ }
+ else
+ {
+ // Refinement scan.
+ ref byte st = ref this.GetFixedBinReference();
+
+ blockDataRef |= (short)(this.DecodeBinaryDecision(ref reader, ref st) << this.SuccessiveLow);
+ }
+ }
+
+ private void DecodeBlockProgressiveAc(ArithmeticDecodingComponent component, ref Block8x8 block, ref ArithmeticDecodingTable acTable)
+ {
+ ref JpegBitReader reader = ref this.scanBuffer;
+ ref short blockDataRef = ref Unsafe.As(ref block);
+
+ ArithmeticStatistics acStatistics = component.AcStatistics;
+ if (acStatistics == null || acTable == null)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("AC table is missing");
+ }
+
+ if (this.SuccessiveHigh == 0)
+ {
+ // Sections F.2.4.2 & F.1.4.4.2: Decoding of AC coefficients.
+
+ // Figure F.20: Decode_AC_coefficients.
+ int start = this.SpectralStart;
+ int end = this.SpectralEnd;
+ int low = this.SuccessiveLow;
+
+ for (int k = start; k <= end; k++)
+ {
+ ref byte st = ref acStatistics.GetReference(3 * (k - 1));
+ if (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ break;
+ }
+
+ while (this.DecodeBinaryDecision(ref reader, ref Unsafe.Add(ref st, 1)) == 0)
+ {
+ st = ref Unsafe.Add(ref st, 3);
+ k++;
+ if (k > 63)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Invalid arithmetic code.");
+ }
+ }
+
+ // Figure F.21: Decoding nonzero value v.
+ // Figure F.22: Decoding the sign of v.
+ int sign = this.DecodeBinaryDecision(ref reader, ref this.GetFixedBinReference());
+ st = ref Unsafe.Add(ref st, 2);
+
+ // Figure F.23: Decoding the magnitude category of v.
+ int m = this.DecodeBinaryDecision(ref reader, ref st);
+ if (m != 0)
+ {
+ if (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ m <<= 1;
+ st = ref acStatistics.GetReference(k <= acTable.AcKx ? 189 : 217);
+ while (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ if ((m <<= 1) == 0x8000)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Invalid arithmetic code.");
+ }
+
+ st = ref Unsafe.Add(ref st, 1);
+ }
+ }
+ }
+
+ int v = m;
+
+ // Figure F.24: Decoding the magnitude bit pattern of v.
+ st = ref Unsafe.Add(ref st, 14);
+ while ((m >>= 1) != 0)
+ {
+ if (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ v |= m;
+ }
+ }
+
+ v += 1;
+ if (sign != 0)
+ {
+ v = -v;
+ }
+
+ Unsafe.Add(ref blockDataRef, ZigZag.TransposingOrder[k]) = (short)(v << low);
+ }
+ }
+ else
+ {
+ // Refinement scan.
+ this.ReadBlockProgressiveAcRefined(acStatistics, ref blockDataRef);
+ }
+ }
+
+ private void ReadBlockProgressiveAcRefined(ArithmeticStatistics acStatistics, ref short blockDataRef)
+ {
+ ref JpegBitReader reader = ref this.scanBuffer;
+ int start = this.SpectralStart;
+ int end = this.SpectralEnd;
+
+ int p1 = 1 << this.SuccessiveLow;
+ int m1 = -1 << this.SuccessiveLow;
+
+ // Establish EOBx (previous stage end-of-block) index.
+ int kex = end;
+ for (; kex > 0; kex--)
+ {
+ if (Unsafe.Add(ref blockDataRef, ZigZag.TransposingOrder[kex]) != 0)
+ {
+ break;
+ }
+ }
+
+ for (int k = start; k <= end; k++)
+ {
+ ref byte st = ref acStatistics.GetReference(3 * (k - 1));
+ if (k > kex)
+ {
+ if (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ break;
+ }
+ }
+
+ while (true)
+ {
+ ref short coef = ref Unsafe.Add(ref blockDataRef, ZigZag.TransposingOrder[k]);
+ if (coef != 0)
+ {
+ if (this.DecodeBinaryDecision(ref reader, ref Unsafe.Add(ref st, 2)) != 0)
+ {
+ coef = (short)(coef + (coef < 0 ? m1 : p1));
+ }
+
+ break;
+ }
+
+ if (this.DecodeBinaryDecision(ref reader, ref Unsafe.Add(ref st, 1)) != 0)
+ {
+ bool flag = this.DecodeBinaryDecision(ref reader, ref this.GetFixedBinReference()) != 0;
+ coef = (short)(coef + (flag ? m1 : p1));
+
+ break;
+ }
+
+ st = ref Unsafe.Add(ref st, 3);
+ k++;
+ if (k > end)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Invalid arithmetic code.");
+ }
+ }
+ }
+ }
+
+ private void DecodeBlockBaseline(
+ ArithmeticDecodingComponent component,
+ ref Block8x8 destinationBlock,
+ ref ArithmeticDecodingTable acTable,
+ ref ArithmeticDecodingTable dcTable)
+ {
+ if (acTable is null)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("AC table is missing.");
+ }
+
+ if (dcTable is null)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("DC table is missing.");
+ }
+
+ ref JpegBitReader reader = ref this.scanBuffer;
+ ref short destinationRef = ref Unsafe.As(ref destinationBlock);
+
+ // Sections F.2.4.1 & F.1.4.4.1: Decoding of DC coefficients.
+
+ // Table F.4: Point to statistics bin S0 for DC coefficient coding.
+ ref byte st = ref Unsafe.Add(ref component.DcStatistics.GetReference(), component.DcContext);
+
+ /* Figure F.19: Decode_DC_DIFF */
+ if (this.DecodeBinaryDecision(ref reader, ref st) == 0)
+ {
+ component.DcContext = 0;
+ }
+ else
+ {
+ // Figure F.21: Decoding nonzero value v
+ // Figure F.22: Decoding the sign of v
+ int sign = this.DecodeBinaryDecision(ref reader, ref Unsafe.Add(ref st, 1));
+ st = ref Unsafe.Add(ref st, (nint)(uint)(2 + sign));
+
+ // Figure F.23: Decoding the magnitude category of v.
+ int m = this.DecodeBinaryDecision(ref reader, ref st);
+ if (m != 0)
+ {
+ // Table F.4: X1 = 20
+ st = ref component.DcStatistics.GetReference(20);
+ while (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ if ((m <<= 1) == 0x8000)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Invalid arithmetic code.");
+ }
+
+ st = ref Unsafe.Add(ref st, 1);
+ }
+ }
+
+ // Section F.1.4.4.1.2: Establish dc_context conditioning category.
+ if (m < (int)((1L << dcTable.DcL) >> 1))
+ {
+ component.DcContext = 0; // zero diff category
+ }
+ else if (m > (int)((1L << dcTable.DcU) >> 1))
+ {
+ component.DcContext = 12 + (sign * 4); // large diff category
+ }
+ else
+ {
+ component.DcContext = 4 + (sign * 4); // small diff category
+ }
+
+ int v = m;
+
+ // Figure F.24: Decoding the magnitude bit pattern of v.
+ st = ref Unsafe.Add(ref st, 14);
+ while ((m >>= 1) != 0)
+ {
+ if (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ v |= m;
+ }
+ }
+
+ v += 1;
+ if (sign != 0)
+ {
+ v = -v;
+ }
+
+ component.DcPredictor = (short)(component.DcPredictor + v);
+ }
+
+ destinationRef = (short)component.DcPredictor;
+
+ // Sections F.2.4.2 & F.1.4.4.2: Decoding of AC coefficients.
+ ArithmeticStatistics acStatistics = component.AcStatistics;
+
+ for (int k = 1; k <= 63; k++)
+ {
+ st = ref acStatistics.GetReference(3 * (k - 1));
+ if (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ // EOB flag.
+ break;
+ }
+
+ while (this.DecodeBinaryDecision(ref reader, ref Unsafe.Add(ref st, 1)) == 0)
+ {
+ st = ref Unsafe.Add(ref st, 3);
+ k++;
+ if (k > 63)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Invalid arithmetic code.");
+ }
+ }
+
+ // Figure F.21: Decoding nonzero value v.
+ // Figure F.22: Decoding the sign of v.
+ int sign = this.DecodeBinaryDecision(ref reader, ref this.GetFixedBinReference());
+ st = ref Unsafe.Add(ref st, 2);
+
+ // Figure F.23: Decoding the magnitude category of v.
+ int m = this.DecodeBinaryDecision(ref reader, ref st);
+ if (m != 0)
+ {
+ if (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ m <<= 1;
+ st = ref acStatistics.GetReference(k <= acTable.AcKx ? 189 : 217);
+ while (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ if ((m <<= 1) == 0x8000)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Invalid arithmetic code.");
+ }
+
+ st = ref Unsafe.Add(ref st, 1);
+ }
+ }
+ }
+
+ int v = m;
+
+ // Figure F.24: Decoding the magnitude bit pattern of v.
+ st = ref Unsafe.Add(ref st, 14);
+ while ((m >>= 1) != 0)
+ {
+ if (this.DecodeBinaryDecision(ref reader, ref st) != 0)
+ {
+ v |= m;
+ }
+ }
+
+ v += 1;
+ if (sign != 0)
+ {
+ v = -v;
+ }
+
+ Unsafe.Add(ref destinationRef, ZigZag.TransposingOrder[k]) = (short)v;
+ }
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private bool HandleRestart()
+ {
+ if (this.restartInterval > 0 && (--this.todo) == 0)
+ {
+ if (this.scanBuffer.Marker == JpegConstants.Markers.XFF)
+ {
+ if (!this.scanBuffer.FindNextMarker())
+ {
+ return false;
+ }
+ }
+
+ this.todo = this.restartInterval;
+
+ foreach (ArithmeticDecodingComponent component in this.components)
+ {
+ component.DcPredictor = 0;
+ component.DcContext = 0;
+ component.DcStatistics?.Reset();
+ component.AcStatistics?.Reset();
+ }
+
+ this.Reset();
+
+ if (this.scanBuffer.HasRestartMarker())
+ {
+ this.Reset();
+ return true;
+ }
+
+ if (this.scanBuffer.HasBadMarker())
+ {
+ this.stream.Position = this.scanBuffer.MarkerPosition;
+ this.Reset();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private void Reset()
+ {
+ for (int i = 0; i < this.components.Length; i++)
+ {
+ var component = this.components[i] as ArithmeticDecodingComponent;
+ component.DcPredictor = 0;
+ }
+
+ this.c = 0;
+ this.a = 0;
+ this.ct = -16; // Force reading 2 initial bytes to fill C.
+
+ this.scanBuffer.Reset();
+ }
+
+ private int DecodeBinaryDecision(ref JpegBitReader reader, ref byte st)
+ {
+ // Renormalization & data input per section D.2.6
+ while (this.a < 0x8000)
+ {
+ if (--this.ct < 0)
+ {
+ // Need to fetch next data byte.
+ reader.CheckBits();
+ int data = reader.GetBits(8);
+
+ // Insert data into C register.
+ this.c = (this.c << 8) | data;
+
+ // Update bit shift counter.
+ if ((this.ct += 8) < 0)
+ {
+ // Need more initial bytes.
+ if (++this.ct == 0)
+ {
+ // Got 2 initial bytes -> re-init A and exit loop
+ this.a = 0x8000; // e->a = 0x10000L after loop exit
+ }
+ }
+ }
+
+ this.a <<= 1;
+ }
+
+ // Fetch values from our compact representation of Table D.3(D.2):
+ // Qe values and probability estimation state machine
+ int sv = st;
+ int qe = ArithmeticTable[sv & 0x7f];
+ byte nl = (byte)qe;
+ qe >>= 8; // Next_Index_LPS + Switch_MPS
+ byte nm = (byte)qe;
+ qe >>= 8; // Next_Index_MPS
+
+ // Decode & estimation procedures per sections D.2.4 & D.2.5
+ int temp = this.a - qe;
+ this.a = temp;
+ temp <<= this.ct;
+ if (this.c >= temp)
+ {
+ this.c -= temp;
+
+ // Conditional LPS (less probable symbol) exchange
+ if (this.a < qe)
+ {
+ this.a = qe;
+ st = (byte)((sv & 0x80) ^ nm); // Estimate_after_MPS
+ }
+ else
+ {
+ this.a = qe;
+ st = (byte)((sv & 0x80) ^ nl); // Estimate_after_LPS
+ sv ^= 0x80; // Exchange LPS/MPS
+ }
+ }
+ else if (this.a < 0x8000)
+ {
+ // Conditional MPS (more probable symbol) exchange
+ if (this.a < qe)
+ {
+ st = (byte)((sv & 0x80) ^ nl); // Estimate_after_LPS
+ sv ^= 0x80; // Exchange LPS/MPS
+ }
+ else
+ {
+ st = (byte)((sv & 0x80) ^ nm); // Estimate_after_MPS
+ }
+ }
+
+ return sv >> 7;
+ }
+
+ // The following function specifies the packing of the four components
+ // into the compact INT32 representation.
+ // Note that this formula must match the actual arithmetic encoder and decoder implementation. The implementation has to be changed
+ // if this formula is changed.
+ // The current organization is leaned on Markus Kuhn's JBIG implementation (jbig_tab.c).
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static int Pack(int a, int b, int c, int d)
+ => (a << 16) | (c << 8) | (d << 7) | b;
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticStatistics.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticStatistics.cs
new file mode 100644
index 000000000..251c1d1c3
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticStatistics.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
+{
+ internal class ArithmeticStatistics
+ {
+ private readonly byte[] statistics;
+
+ public ArithmeticStatistics(bool dc, int identifier)
+ {
+ this.IsDcStatistics = dc;
+ this.Identifier = identifier;
+ this.statistics = dc ? new byte[64] : new byte[256];
+ }
+
+ public bool IsDcStatistics { get; private set; }
+
+ public int Identifier { get; private set; }
+
+ public ref byte GetReference() => ref this.statistics[0];
+
+ public ref byte GetReference(int offset) => ref this.statistics[offset];
+
+ public void Reset() => this.statistics.AsSpan().Clear();
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx.cs
index 7366ee30a..7955600b5 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykScalar.cs
index 68dfa9bfb..65301c1b3 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykScalar.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykScalar.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector.cs
index 6b7ed169e..54be7512c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs
index 963543ad4..261d76a31 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs
index 3f6a6caa4..e1d94727c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs
index c484aac28..a96fc8caf 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs
index f017716e3..93dfe4688 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbScalar.cs
index 24c59206d..c97da4f62 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbScalar.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbScalar.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector.cs
index ff3a2bee1..24d430f0a 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs
index 892bcc79e..2167c8686 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs
index 4b6d88f72..2f8533c6c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector.cs
index 48e311d99..8559ac987 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx.cs
index 1f18d5324..8b5f8463d 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKScalar.cs
index d6387ae71..90d2ac82d 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKScalar.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKScalar.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector.cs
index 66c79ae7c..e2e00c213 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterAvx.cs
index 81c7c0764..b333d3268 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterAvx.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterAvx.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
+using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
@@ -25,7 +26,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
- public override bool IsAvailable => Avx.IsSupported;
+ public sealed override bool IsAvailable => Avx.IsSupported;
+
+ public sealed override int ElementsPerBatch => Vector256.Count;
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterBase.cs
index 808ca687b..8339206b4 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterBase.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterBase.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Collections.Generic;
@@ -35,6 +35,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
///
public abstract bool IsAvailable { get; }
+ ///
+ /// Gets a value indicating how many pixels are processed in a single batch.
+ ///
+ ///
+ /// This generally should be equal to register size,
+ /// e.g. 1 for scalar implementation, 8 for AVX implementation and so on.
+ ///
+ public abstract int ElementsPerBatch { get; }
+
///
/// Gets the of this converter.
///
@@ -219,7 +228,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
///
/// List of component color processors.
/// Row to convert
- public ComponentValues(IReadOnlyList processors, int row)
+ public ComponentValues(IReadOnlyList processors, int row)
{
DebugGuard.MustBeGreaterThan(processors.Count, 0, nameof(processors));
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterScalar.cs
index ff88ab969..8cf8ad1d9 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterScalar.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterScalar.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
@@ -16,7 +16,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
- public override bool IsAvailable => true;
+ public sealed override bool IsAvailable => true;
+
+ public sealed override int ElementsPerBatch => 1;
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterVector.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterVector.cs
index ca482d78d..6e0c0cff3 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterVector.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterVector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// Even though real life data is guaranteed to be of size
/// divisible by 8 newer SIMD instructions like AVX512 won't work with
/// such data out of the box. These converters have fallback code
- /// for 'remainder' data.
+ /// for remainder data.
///
internal abstract class JpegColorConverterVector : JpegColorConverterBase
{
@@ -28,7 +28,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
public sealed override bool IsAvailable => Vector.IsHardwareAccelerated && Vector.Count % 4 == 0;
- public override void ConvertToRgbInplace(in ComponentValues values)
+ public sealed override int ElementsPerBatch => Vector.Count;
+
+ public sealed override void ConvertToRgbInplace(in ComponentValues values)
{
DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware.");
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/ComponentProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/ComponentProcessor.cs
new file mode 100644
index 000000000..87e85686c
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/ComponentProcessor.cs
@@ -0,0 +1,65 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
+{
+ ///
+ /// Base class for processing component spectral data and converting it to raw color data.
+ ///
+ internal abstract class ComponentProcessor : IDisposable
+ {
+ public ComponentProcessor(MemoryAllocator memoryAllocator, JpegFrame frame, Size postProcessorBufferSize, IJpegComponent component, int blockSize)
+ {
+ this.Frame = frame;
+ this.Component = component;
+
+ this.BlockAreaSize = component.SubSamplingDivisors * blockSize;
+ this.ColorBuffer = memoryAllocator.Allocate2DOveraligned(
+ postProcessorBufferSize.Width,
+ postProcessorBufferSize.Height,
+ this.BlockAreaSize.Height);
+ }
+
+ protected JpegFrame Frame { get; }
+
+ protected IJpegComponent Component { get; }
+
+ protected Buffer2D ColorBuffer { get; }
+
+ protected Size BlockAreaSize { get; }
+
+ ///
+ /// Converts spectral data to color data accessible via .
+ ///
+ /// Spectral row index to convert.
+ public abstract void CopyBlocksToColorBuffer(int row);
+
+ ///
+ /// Clears spectral buffers.
+ ///
+ ///
+ /// Should only be called during baseline interleaved decoding.
+ ///
+ public void ClearSpectralBuffers()
+ {
+ Buffer2D spectralBlocks = this.Component.SpectralBlocks;
+ for (int i = 0; i < spectralBlocks.Height; i++)
+ {
+ spectralBlocks.DangerousGetRowSpan(i).Clear();
+ }
+ }
+
+ ///
+ /// Gets converted color buffer row.
+ ///
+ /// Row index.
+ /// Color buffer row.
+ public Span GetColorBufferRowSpan(int row) =>
+ this.ColorBuffer.DangerousGetRowSpan(row);
+
+ public void Dispose() => this.ColorBuffer.Dispose();
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DirectComponentProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DirectComponentProcessor.cs
new file mode 100644
index 000000000..80cc689e3
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DirectComponentProcessor.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
+{
+ ///
+ /// Processes component spectral data and converts it to color data in 1-to-1 scale.
+ ///
+ internal sealed class DirectComponentProcessor : ComponentProcessor
+ {
+ private Block8x8F dequantizationTable;
+
+ public DirectComponentProcessor(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
+ : base(memoryAllocator, frame, postProcessorBufferSize, component, blockSize: 8)
+ {
+ this.dequantizationTable = rawJpeg.QuantizationTables[component.QuantizationTableIndex];
+ FloatingPointDCT.AdjustToIDCT(ref this.dequantizationTable);
+ }
+
+ public override void CopyBlocksToColorBuffer(int spectralStep)
+ {
+ Buffer2D spectralBuffer = this.Component.SpectralBlocks;
+
+ float maximumValue = this.Frame.MaxColorChannelValue;
+
+ int destAreaStride = this.ColorBuffer.Width;
+
+ int blocksRowsPerStep = this.Component.SamplingFactors.Height;
+
+ int yBlockStart = spectralStep * blocksRowsPerStep;
+
+ Size subSamplingDivisors = this.Component.SubSamplingDivisors;
+
+ Block8x8F workspaceBlock = default;
+
+ for (int y = 0; y < blocksRowsPerStep; y++)
+ {
+ int yBuffer = y * this.BlockAreaSize.Height;
+
+ Span colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer);
+ Span blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y);
+
+ for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
+ {
+ // Integer to float
+ workspaceBlock.LoadFrom(ref blockRow[xBlock]);
+
+ // Dequantize
+ workspaceBlock.MultiplyInPlace(ref this.dequantizationTable);
+
+ // Convert from spectral to color
+ FloatingPointDCT.TransformIDCT(ref workspaceBlock);
+
+ // To conform better to libjpeg we actually NEED TO loose precision here.
+ // This is because they store blocks as Int16 between all the operations.
+ // To be "more accurate", we need to emulate this by rounding!
+ workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue);
+
+ // Write to color buffer acording to sampling factors
+ int xColorBufferStart = xBlock * this.BlockAreaSize.Width;
+ workspaceBlock.ScaledCopyTo(
+ ref colorBufferRow[xColorBufferStart],
+ destAreaStride,
+ subSamplingDivisors.Width,
+ subSamplingDivisors.Height);
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs
new file mode 100644
index 000000000..801b2a3fb
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs
@@ -0,0 +1,102 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
+{
+ ///
+ /// Processes component spectral data and converts it to color data in 2-to-1 scale.
+ ///
+ internal sealed class DownScalingComponentProcessor2 : ComponentProcessor
+ {
+ private Block8x8F dequantizationTable;
+
+ public DownScalingComponentProcessor2(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
+ : base(memoryAllocator, frame, postProcessorBufferSize, component, 4)
+ {
+ this.dequantizationTable = rawJpeg.QuantizationTables[component.QuantizationTableIndex];
+ ScaledFloatingPointDCT.AdjustToIDCT(ref this.dequantizationTable);
+ }
+
+ public override void CopyBlocksToColorBuffer(int spectralStep)
+ {
+ Buffer2D spectralBuffer = this.Component.SpectralBlocks;
+
+ float maximumValue = this.Frame.MaxColorChannelValue;
+ float normalizationValue = MathF.Ceiling(maximumValue / 2);
+
+ int destAreaStride = this.ColorBuffer.Width;
+
+ int blocksRowsPerStep = this.Component.SamplingFactors.Height;
+ Size subSamplingDivisors = this.Component.SubSamplingDivisors;
+
+ Block8x8F workspaceBlock = default;
+
+ int yBlockStart = spectralStep * blocksRowsPerStep;
+
+ for (int y = 0; y < blocksRowsPerStep; y++)
+ {
+ int yBuffer = y * this.BlockAreaSize.Height;
+
+ Span colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer);
+ Span blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y);
+
+ for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
+ {
+ // Integer to float
+ workspaceBlock.LoadFrom(ref blockRow[xBlock]);
+
+ // IDCT/Normalization/Range
+ ScaledFloatingPointDCT.TransformIDCT_4x4(ref workspaceBlock, ref this.dequantizationTable, normalizationValue, maximumValue);
+
+ // Save to the intermediate buffer
+ int xColorBufferStart = xBlock * this.BlockAreaSize.Width;
+ ScaledCopyTo(
+ ref workspaceBlock,
+ ref colorBufferRow[xColorBufferStart],
+ destAreaStride,
+ subSamplingDivisors.Width,
+ subSamplingDivisors.Height);
+ }
+ }
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void ScaledCopyTo(ref Block8x8F block, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
+ {
+ // TODO: Optimize: implement all cases with scale-specific, loopless code!
+ CopyArbitraryScale(ref block, ref destRef, destStrideWidth, horizontalScale, verticalScale);
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ int yy = y * verticalScale;
+ int y8 = y * 8;
+
+ for (int x = 0; x < 4; x++)
+ {
+ int xx = x * horizontalScale;
+
+ float value = block[y8 + x];
+
+ for (int i = 0; i < verticalScale; i++)
+ {
+ int baseIdx = ((yy + i) * areaStride) + xx;
+
+ for (int j = 0; j < horizontalScale; j++)
+ {
+ // area[xx + j, yy + i] = value;
+ Unsafe.Add(ref areaOrigin, (nint)(uint)(baseIdx + j)) = value;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs
new file mode 100644
index 000000000..1c63abc93
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs
@@ -0,0 +1,102 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
+{
+ ///
+ /// Processes component spectral data and converts it to color data in 4-to-1 scale.
+ ///
+ internal sealed class DownScalingComponentProcessor4 : ComponentProcessor
+ {
+ private Block8x8F dequantizationTable;
+
+ public DownScalingComponentProcessor4(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
+ : base(memoryAllocator, frame, postProcessorBufferSize, component, 2)
+ {
+ this.dequantizationTable = rawJpeg.QuantizationTables[component.QuantizationTableIndex];
+ ScaledFloatingPointDCT.AdjustToIDCT(ref this.dequantizationTable);
+ }
+
+ public override void CopyBlocksToColorBuffer(int spectralStep)
+ {
+ Buffer2D spectralBuffer = this.Component.SpectralBlocks;
+
+ float maximumValue = this.Frame.MaxColorChannelValue;
+ float normalizationValue = MathF.Ceiling(maximumValue / 2);
+
+ int destAreaStride = this.ColorBuffer.Width;
+
+ int blocksRowsPerStep = this.Component.SamplingFactors.Height;
+ Size subSamplingDivisors = this.Component.SubSamplingDivisors;
+
+ Block8x8F workspaceBlock = default;
+
+ int yBlockStart = spectralStep * blocksRowsPerStep;
+
+ for (int y = 0; y < blocksRowsPerStep; y++)
+ {
+ int yBuffer = y * this.BlockAreaSize.Height;
+
+ Span colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer);
+ Span blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y);
+
+ for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
+ {
+ // Integer to float
+ workspaceBlock.LoadFrom(ref blockRow[xBlock]);
+
+ // IDCT/Normalization/Range
+ ScaledFloatingPointDCT.TransformIDCT_2x2(ref workspaceBlock, ref this.dequantizationTable, normalizationValue, maximumValue);
+
+ // Save to the intermediate buffer
+ int xColorBufferStart = xBlock * this.BlockAreaSize.Width;
+ ScaledCopyTo(
+ ref workspaceBlock,
+ ref colorBufferRow[xColorBufferStart],
+ destAreaStride,
+ subSamplingDivisors.Width,
+ subSamplingDivisors.Height);
+ }
+ }
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void ScaledCopyTo(ref Block8x8F block, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
+ {
+ // TODO: Optimize: implement all cases with scale-specific, loopless code!
+ CopyArbitraryScale(ref block, ref destRef, destStrideWidth, horizontalScale, verticalScale);
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
+ {
+ for (int y = 0; y < 2; y++)
+ {
+ int yy = y * verticalScale;
+ int y8 = y * 8;
+
+ for (int x = 0; x < 2; x++)
+ {
+ int xx = x * horizontalScale;
+
+ float value = block[y8 + x];
+
+ for (int i = 0; i < verticalScale; i++)
+ {
+ int baseIdx = ((yy + i) * areaStride) + xx;
+
+ for (int j = 0; j < horizontalScale; j++)
+ {
+ // area[xx + j, yy + i] = value;
+ Unsafe.Add(ref areaOrigin, (nint)(uint)(baseIdx + j)) = value;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs
new file mode 100644
index 000000000..03f0de741
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs
@@ -0,0 +1,88 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
+{
+ ///
+ /// Processes component spectral data and converts it to color data in 8-to-1 scale.
+ ///
+ internal sealed class DownScalingComponentProcessor8 : ComponentProcessor
+ {
+ private readonly float dcDequantizatizer;
+
+ public DownScalingComponentProcessor8(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
+ : base(memoryAllocator, frame, postProcessorBufferSize, component, 1)
+ => this.dcDequantizatizer = 0.125f * rawJpeg.QuantizationTables[component.QuantizationTableIndex][0];
+
+ public override void CopyBlocksToColorBuffer(int spectralStep)
+ {
+ Buffer2D spectralBuffer = this.Component.SpectralBlocks;
+
+ float maximumValue = this.Frame.MaxColorChannelValue;
+ float normalizationValue = MathF.Ceiling(maximumValue / 2);
+
+ int destAreaStride = this.ColorBuffer.Width;
+
+ int blocksRowsPerStep = this.Component.SamplingFactors.Height;
+ Size subSamplingDivisors = this.Component.SubSamplingDivisors;
+
+ int yBlockStart = spectralStep * blocksRowsPerStep;
+
+ for (int y = 0; y < blocksRowsPerStep; y++)
+ {
+ int yBuffer = y * this.BlockAreaSize.Height;
+
+ Span colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer);
+ Span blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y);
+
+ for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
+ {
+ float dc = ScaledFloatingPointDCT.TransformIDCT_1x1(blockRow[xBlock][0], this.dcDequantizatizer, normalizationValue, maximumValue);
+
+ // Save to the intermediate buffer
+ int xColorBufferStart = xBlock * this.BlockAreaSize.Width;
+ ScaledCopyTo(
+ dc,
+ ref colorBufferRow[xColorBufferStart],
+ destAreaStride,
+ subSamplingDivisors.Width,
+ subSamplingDivisors.Height);
+ }
+ }
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void ScaledCopyTo(float value, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
+ {
+ if (horizontalScale == 1 && verticalScale == 1)
+ {
+ destRef = value;
+ return;
+ }
+
+ if (horizontalScale == 2 && verticalScale == 2)
+ {
+ destRef = value;
+ Unsafe.Add(ref destRef, 1) = value;
+ Unsafe.Add(ref destRef, 0 + (nint)(uint)destStrideWidth) = value;
+ Unsafe.Add(ref destRef, 1 + (nint)(uint)destStrideWidth) = value;
+ return;
+ }
+
+ // TODO: Optimize: implement all cases with scale-specific, loopless code!
+ for (int y = 0; y < verticalScale; y++)
+ {
+ for (int x = 0; x < horizontalScale; x++)
+ {
+ Unsafe.Add(ref destRef, (nint)(uint)x) = value;
+ }
+
+ destRef = ref Unsafe.Add(ref destRef, (nint)(uint)destStrideWidth);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
index e1faf93f4..6f57dff99 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
@@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// Originally ported from
/// with additional fixes for both performance and common encoding errors.
///
- internal class HuffmanScanDecoder
+ internal class HuffmanScanDecoder : IJpegScanDecoder
{
private readonly BufferedReadStream stream;
@@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Shortcut for .Components.
///
- private JpegComponent[] components;
+ private IJpegComponent[] components;
///
/// Number of component in the current scan.
@@ -54,11 +54,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private readonly HuffmanTable[] dcHuffmanTables;
///
- /// The AC Huffman tables
+ /// The AC Huffman tables.
///
private readonly HuffmanTable[] acHuffmanTables;
- private HuffmanScanBuffer scanBuffer;
+ private JpegBitReader scanBuffer;
private readonly SpectralConverter spectralConverter;
@@ -109,20 +109,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// The successive approximation low bit end.
public int SuccessiveLow { get; set; }
- ///
- /// Decodes the entropy coded data.
- ///
- /// Component count in the current scan.
+ ///
public void ParseEntropyCodedData(int scanComponentCount)
{
this.cancellationToken.ThrowIfCancellationRequested();
this.scanComponentCount = scanComponentCount;
- this.scanBuffer = new HuffmanScanBuffer(this.stream);
+ this.scanBuffer = new JpegBitReader(this.stream);
- bool fullScan = this.frame.Progressive || this.frame.MultiScan;
- this.frame.AllocateComponents(fullScan);
+ this.frame.AllocateComponents();
if (!this.frame.Progressive)
{
@@ -139,6 +135,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
+ ///
public void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
{
this.frame = frame;
@@ -151,11 +148,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
if (this.scanComponentCount != 1)
{
+ this.spectralConverter.PrepareForDecoding();
this.ParseBaselineDataInterleaved();
this.spectralConverter.CommitConversion();
}
else if (this.frame.ComponentCount == 1)
{
+ this.spectralConverter.PrepareForDecoding();
this.ParseBaselineDataSingleComponent();
this.spectralConverter.CommitConversion();
}
@@ -170,7 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0;
int mcusPerColumn = this.frame.McusPerColumn;
int mcusPerLine = this.frame.McusPerLine;
- ref HuffmanScanBuffer buffer = ref this.scanBuffer;
+ ref JpegBitReader buffer = ref this.scanBuffer;
for (int j = 0; j < mcusPerColumn; j++)
{
@@ -184,10 +183,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int k = 0; k < this.scanComponentCount; k++)
{
int order = this.frame.ComponentOrder[k];
- JpegComponent component = this.components[order];
+ var component = this.components[order] as JpegComponent;
- ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
- ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
+ ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DcTableId];
+ ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.AcTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
@@ -233,14 +232,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void ParseBaselineDataNonInterleaved()
{
- JpegComponent component = this.components[this.frame.ComponentOrder[0]];
- ref HuffmanScanBuffer buffer = ref this.scanBuffer;
+ var component = this.components[this.frame.ComponentOrder[0]] as JpegComponent;
+ ref JpegBitReader buffer = ref this.scanBuffer;
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 HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DcTableId];
+ ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.AcTableId];
for (int j = 0; j < h; j++)
{
@@ -272,10 +271,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcuLines = this.frame.McusPerColumn;
int w = component.WidthInBlocks;
int h = component.SamplingFactors.Height;
- ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
- ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
+ ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DcTableId];
+ ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.AcTableId];
- ref HuffmanScanBuffer buffer = ref this.scanBuffer;
+ ref JpegBitReader buffer = ref this.scanBuffer;
for (int i = 0; i < mcuLines; i++)
{
@@ -382,7 +381,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0;
int mcusPerColumn = this.frame.McusPerColumn;
int mcusPerLine = this.frame.McusPerLine;
- ref HuffmanScanBuffer buffer = ref this.scanBuffer;
+ ref JpegBitReader buffer = ref this.scanBuffer;
for (int j = 0; j < mcusPerColumn; j++)
{
@@ -394,8 +393,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int k = 0; k < this.scanComponentCount; k++)
{
int order = this.frame.ComponentOrder[k];
- JpegComponent component = this.components[order];
- ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
+ var component = this.components[order] as JpegComponent;
+ ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DcTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
@@ -435,15 +434,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void ParseProgressiveDataNonInterleaved()
{
- JpegComponent component = this.components[this.frame.ComponentOrder[0]];
- ref HuffmanScanBuffer buffer = ref this.scanBuffer;
+ var component = this.components[this.frame.ComponentOrder[0]] as JpegComponent;
+ ref JpegBitReader buffer = ref this.scanBuffer;
int w = component.WidthInBlocks;
int h = component.HeightInBlocks;
if (this.SpectralStart == 0)
{
- ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
+ ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DcTableId];
for (int j = 0; j < h; j++)
{
@@ -470,7 +469,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
else
{
- ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
+ ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.AcTableId];
for (int j = 0; j < h; j++)
{
@@ -503,7 +502,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref HuffmanTable acTable)
{
ref short blockDataRef = ref Unsafe.As(ref block);
- ref HuffmanScanBuffer buffer = ref this.scanBuffer;
+ ref JpegBitReader buffer = ref this.scanBuffer;
// DC
int t = buffer.DecodeHuffman(ref dcTable);
@@ -545,7 +544,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockProgressiveDC(JpegComponent component, ref Block8x8 block, ref HuffmanTable dcTable)
{
ref short blockDataRef = ref Unsafe.As(ref block);
- ref HuffmanScanBuffer buffer = ref this.scanBuffer;
+ ref JpegBitReader buffer = ref this.scanBuffer;
if (this.SuccessiveHigh == 0)
{
@@ -581,7 +580,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- ref HuffmanScanBuffer buffer = ref this.scanBuffer;
+ ref JpegBitReader buffer = ref this.scanBuffer;
int start = this.SpectralStart;
int end = this.SpectralEnd;
int low = this.SuccessiveLow;
@@ -626,7 +625,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockProgressiveACRefined(ref short blockDataRef, ref HuffmanTable acTable)
{
// Refinement scan for these AC coefficients
- ref HuffmanScanBuffer buffer = ref this.scanBuffer;
+ ref JpegBitReader buffer = ref this.scanBuffer;
int start = this.SpectralStart;
int end = this.SpectralEnd;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
index bee5e0229..5ef9f760c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
@@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Contains the largest code of length k (0 if none). MaxCode[17] is a sentinel to
- /// ensure terminates.
+ /// ensure terminates.
///
public fixed ulong MaxCode[18];
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
index 54077339d..2e4215325 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Memory;
@@ -10,6 +10,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
internal interface IJpegComponent
{
+ ///
+ /// Gets the component id.
+ ///
+ byte Id { get; }
+
///
/// Gets the component's position in the components array.
///
@@ -25,6 +30,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
Size SamplingFactors { get; }
+ ///
+ /// Gets the horizontal sampling factor.
+ ///
+ int HorizontalSamplingFactor { get; }
+
+ ///
+ /// Gets the vertical sampling factor.
+ ///
+ int VerticalSamplingFactor { get; }
+
///
/// Gets the divisors needed to apply when calculating colors.
///
@@ -44,5 +59,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// We need to apply IDCT and dequantization to transform them into color-space blocks.
///
Buffer2D SpectralBlocks { get; }
+
+ ///
+ /// Gets or sets DC coefficient predictor.
+ ///
+ int DcPredictor { get; set; }
+
+ ///
+ /// Gets or sets the index for the DC table.
+ ///
+ int DcTableId { get; set; }
+
+ ///
+ /// Gets or sets the index for the AC table.
+ ///
+ int AcTableId { get; set; }
+
+ ///
+ /// Initializes component for future buffers initialization.
+ ///
+ /// Maximal horizontal subsampling factor among all the components.
+ /// Maximal vertical subsampling factor among all the components.
+ void Init(int maxSubFactorH, int maxSubFactorV);
+
+ ///
+ /// Allocates the spectral blocks.
+ ///
+ /// if set to true, use the full height of a block, otherwise use the vertical sampling factor.
+ void AllocateSpectral(bool fullScan);
+
+ ///
+ /// Releases resources.
+ ///
+ void Dispose();
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegScanDecoder.cs
new file mode 100644
index 000000000..e270198b7
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegScanDecoder.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
+{
+ ///
+ /// Interface for a JPEG scan decoder.
+ ///
+ internal interface IJpegScanDecoder
+ {
+ ///
+ /// Sets the reset interval.
+ ///
+ int ResetInterval { set; }
+
+ ///
+ /// Gets or sets the spectral selection start.
+ ///
+ int SpectralStart { get; set; }
+
+ ///
+ /// Gets or sets the spectral selection end.
+ ///
+ int SpectralEnd { get; set; }
+
+ ///
+ /// Gets or sets the successive approximation high bit end.
+ ///
+ int SuccessiveHigh { get; set; }
+
+ ///
+ /// Gets or sets the successive approximation low bit end.
+ ///
+ int SuccessiveLow { get; set; }
+
+ ///
+ /// Decodes the entropy coded data.
+ ///
+ /// Component count in the current scan.
+ void ParseEntropyCodedData(int scanComponentCount);
+
+ ///
+ /// Sets the JpegFrame and its components and injects the frame data into the spectral converter.
+ ///
+ /// The frame.
+ /// The raw JPEG data.
+ void InjectFrameData(JpegFrame frame, IRawJpegData jpegData);
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
index 33815e539..cbbb44ffb 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
@@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Gets the components.
///
- IJpegComponent[] Components { get; }
+ JpegComponent[] Components { get; }
///
/// Gets the quantization tables, in natural order.
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
index a95e6c16c..89d3d412d 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using SixLabors.ImageSharp.Metadata;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs
similarity index 95%
rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs
rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs
index 3664cb4eb..b5e9009ec 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.IO;
@@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Used to buffer and track the bits read from the Huffman entropy encoded data.
///
- internal struct HuffmanScanBuffer
+ internal struct JpegBitReader
{
private readonly BufferedReadStream stream;
@@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// Whether there is no more good data to pull from the stream for the current mcu.
private bool badData;
- public HuffmanScanBuffer(BufferedReadStream stream)
+ public JpegBitReader(BufferedReadStream stream)
{
this.stream = stream;
this.data = 0ul;
@@ -155,12 +155,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
c = this.ReadStream();
}
+ // Found a marker
// We accept multiple FF bytes followed by a 0 as meaning a single FF data byte.
- // This data pattern is not valid according to the standard.
+ // even though it's considered 'invalid' according to the specs.
if (c != 0)
{
- this.Marker = (byte)c;
+ // It's a trick so we won't read past actual marker
this.badData = true;
+ this.Marker = (byte)c;
this.MarkerPosition = this.stream.Position - 2;
}
}
@@ -199,7 +201,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
if (b != 0)
{
this.Marker = (byte)b;
- this.badData = true;
this.MarkerPosition = this.stream.Position - 2;
return true;
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs
index 7ef280932..287f36991 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
index 3804e1c6c..82f6be754 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using SixLabors.ImageSharp.Memory;
@@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Represents a single frame component.
///
- internal sealed class JpegComponent : IDisposable, IJpegComponent
+ internal class JpegComponent : IDisposable, IJpegComponent
{
private readonly MemoryAllocator memoryAllocator;
@@ -78,12 +78,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Gets or sets the index for the DC Huffman table.
///
- public int DCHuffmanTableId { get; set; }
+ public int DcTableId { get; set; }
///
/// Gets or sets the index for the AC Huffman table.
///
- public int ACHuffmanTableId { get; set; }
+ public int AcTableId { get; set; }
public JpegFrame Frame { get; }
@@ -119,11 +119,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
+ ///
public void AllocateSpectral(bool fullScan)
{
if (this.SpectralBlocks != null)
{
- // this method will be called each scan marker so we need to allocate only once
+ // This method will be called each scan marker so we need to allocate only once.
return;
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
deleted file mode 100644
index c3bf1cbdd..000000000
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using SixLabors.ImageSharp.Memory;
-
-namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
-{
- ///
- /// Encapsulates spectral data to rgba32 processing for one component.
- ///
- internal class JpegComponentPostProcessor : IDisposable
- {
- ///
- /// The size of the area in corresponding to one 8x8 Jpeg block
- ///
- private readonly Size blockAreaSize;
-
- ///
- /// Jpeg frame instance containing required decoding metadata.
- ///
- private readonly JpegFrame frame;
-
- ///
- /// Gets the maximal number of block rows being processed in one step.
- ///
- private readonly int blockRowsPerStep;
-
- ///
- /// Gets the component containing decoding meta information.
- ///
- private readonly IJpegComponent component;
-
- ///
- /// Gets the instance containing decoding meta information.
- ///
- private readonly IRawJpegData rawJpeg;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public JpegComponentPostProcessor(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
- {
- this.frame = frame;
-
- this.component = component;
- this.rawJpeg = rawJpeg;
- this.blockAreaSize = this.component.SubSamplingDivisors * 8;
- this.ColorBuffer = memoryAllocator.Allocate2DOveraligned(
- postProcessorBufferSize.Width,
- postProcessorBufferSize.Height,
- this.blockAreaSize.Height);
-
- this.blockRowsPerStep = postProcessorBufferSize.Height / 8 / this.component.SubSamplingDivisors.Height;
- }
-
- ///
- /// Gets the temporary working buffer of color values.
- ///
- public Buffer2D ColorBuffer { get; }
-
- ///
- public void Dispose() => this.ColorBuffer.Dispose();
-
- ///
- /// Convert raw spectral DCT data to color data and copy it to the color buffer .
- ///
- public void CopyBlocksToColorBuffer(int spectralStep)
- {
- Buffer2D spectralBuffer = this.component.SpectralBlocks;
-
- float maximumValue = this.frame.MaxColorChannelValue;
-
- int destAreaStride = this.ColorBuffer.Width;
-
- int yBlockStart = spectralStep * this.blockRowsPerStep;
-
- Size subSamplingDivisors = this.component.SubSamplingDivisors;
-
- Block8x8F dequantTable = this.rawJpeg.QuantizationTables[this.component.QuantizationTableIndex];
- Block8x8F workspaceBlock = default;
-
- for (int y = 0; y < this.blockRowsPerStep; y++)
- {
- int yBuffer = y * this.blockAreaSize.Height;
-
- Span colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer);
- Span blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y);
-
- for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
- {
- // Integer to float
- workspaceBlock.LoadFrom(ref blockRow[xBlock]);
-
- // Dequantize
- workspaceBlock.MultiplyInPlace(ref dequantTable);
-
- // Convert from spectral to color
- FastFloatingPointDCT.TransformIDCT(ref workspaceBlock);
-
- // To conform better to libjpeg we actually NEED TO loose precision here.
- // This is because they store blocks as Int16 between all the operations.
- // To be "more accurate", we need to emulate this by rounding!
- workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue);
-
- // Write to color buffer acording to sampling factors
- int xColorBufferStart = xBlock * this.blockAreaSize.Width;
- workspaceBlock.ScaledCopyTo(
- ref colorBufferRow[xColorBufferStart],
- destAreaStride,
- subSamplingDivisors.Width,
- subSamplingDivisors.Height);
- }
- }
- }
-
- public void ClearSpectralBuffers()
- {
- Buffer2D spectralBlocks = this.component.SpectralBlocks;
- for (int i = 0; i < spectralBlocks.Height; i++)
- {
- spectralBlocks.DangerousGetRowSpan(i).Clear();
- }
- }
-
- public Span GetColorBufferRowSpan(int row) =>
- this.ColorBuffer.DangerousGetRowSpan(row);
- }
-}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs
index 811543764..f3ceb3584 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
index fc109be26..5f55a1063 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
@@ -1,19 +1,19 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
///
- /// Represent a single jpeg frame
+ /// Represent a single jpeg frame.
///
internal sealed class JpegFrame : IDisposable
{
public JpegFrame(JpegFileMarker sofMarker, byte precision, int width, int height, byte componentCount)
{
- this.Extended = sofMarker.Marker == JpegConstants.Markers.SOF1;
- this.Progressive = sofMarker.Marker == JpegConstants.Markers.SOF2;
+ this.Extended = sofMarker.Marker is JpegConstants.Markers.SOF1 or JpegConstants.Markers.SOF9;
+ this.Progressive = sofMarker.Marker is JpegConstants.Markers.SOF2 or JpegConstants.Markers.SOF10;
this.Precision = precision;
this.MaxColorChannelValue = MathF.Pow(2, precision) - 1;
@@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Gets the pixel size of the image.
///
- public Size PixelSize => new Size(this.PixelWidth, this.PixelHeight);
+ public Size PixelSize => new(this.PixelWidth, this.PixelHeight);
///
/// Gets the number of components within a frame.
@@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Gets the mcu size of the image.
///
- public Size McuSize => new Size(this.McusPerLine, this.McusPerColumn);
+ public Size McuSize => new(this.McusPerLine, this.McusPerColumn);
///
/// Gets the color depth, in number of bits per pixel.
@@ -134,16 +134,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int i = 0; i < this.ComponentCount; i++)
{
- JpegComponent component = this.Components[i];
+ IJpegComponent component = this.Components[i];
component.Init(maxSubFactorH, maxSubFactorV);
}
}
- public void AllocateComponents(bool fullScan)
+ public void AllocateComponents()
{
+ bool fullScan = this.Progressive || this.MultiScan;
for (int i = 0; i < this.ComponentCount; i++)
{
- JpegComponent component = this.Components[i];
+ IJpegComponent component = this.Components[i];
component.AllocateSpectral(fullScan);
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
index b41c949b2..9f1d4d91f 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs
index acd98bcfc..bef23d89b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
@@ -10,6 +10,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
internal abstract class SpectralConverter
{
+ ///
+ /// Supported scaled spectral block sizes for scaled IDCT decoding.
+ ///
+ private static readonly int[] ScaledBlockSizes = new int[]
+ {
+ // 8 => 1, 1/8 of the original size
+ 1,
+
+ // 8 => 2, 1/4 of the original size
+ 2,
+
+ // 8 => 4, 1/2 of the original size
+ 4,
+ };
+
///
/// Gets a value indicating whether this converter has converted spectral
/// data of the current image or not.
@@ -20,12 +35,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// Injects jpeg image decoding metadata.
///
///
- /// This is guaranteed to be called only once at SOF marker by .
+ /// This should be called exactly once during SOF (Start Of Frame) marker.
///
- /// instance containing decoder-specific parameters.
- /// instance containing decoder-specific parameters.
+ /// Instance containing decoder-specific parameters.
+ /// Instance containing decoder-specific parameters.
public abstract void InjectFrameData(JpegFrame frame, IRawJpegData jpegData);
+ ///
+ /// Initializes this spectral decoder instance for decoding.
+ /// This should be called exactly once after all markers which can alter
+ /// spectral decoding parameters.
+ ///
+ public abstract void PrepareForDecoding();
+
///
/// Converts single spectral jpeg stride to color stride in baseline
/// decoding mode.
@@ -58,5 +80,48 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// The raw JPEG data.
/// The color converter.
protected virtual JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverterBase.GetConverter(jpegData.ColorSpace, frame.Precision);
+
+ ///
+ /// Calculates image size with optional scaling.
+ ///
+ ///
+ /// Does not apply scalling if is null.
+ ///
+ /// Size of the image.
+ /// Target size of the image.
+ /// Spectral block size, equals to 8 if scaling is not applied.
+ /// Resulting image size, equals to if scaling is not applied.
+ public static Size CalculateResultingImageSize(Size size, Size? targetSize, out int blockPixelSize)
+ {
+ const int blockNativePixelSize = 8;
+
+ blockPixelSize = blockNativePixelSize;
+ if (targetSize != null)
+ {
+ Size tSize = targetSize.Value;
+
+ int fullBlocksWidth = (int)((uint)size.Width / blockNativePixelSize);
+ int fullBlocksHeight = (int)((uint)size.Height / blockNativePixelSize);
+
+ // & (blockNativePixelSize - 1) is Numerics.Modulo8(), basically
+ int blockWidthRemainder = size.Width & (blockNativePixelSize - 1);
+ int blockHeightRemainder = size.Height & (blockNativePixelSize - 1);
+
+ for (int i = 0; i < ScaledBlockSizes.Length; i++)
+ {
+ int blockSize = ScaledBlockSizes[i];
+ int scaledWidth = (fullBlocksWidth * blockSize) + (int)Numerics.DivideCeil((uint)(blockWidthRemainder * blockSize), blockNativePixelSize);
+ int scaledHeight = (fullBlocksHeight * blockSize) + (int)Numerics.DivideCeil((uint)(blockHeightRemainder * blockSize), blockNativePixelSize);
+
+ if (scaledWidth >= tSize.Width && scaledHeight >= tSize.Height)
+ {
+ blockPixelSize = blockSize;
+ return new Size(scaledWidth, scaledHeight);
+ }
+ }
+ }
+
+ return size;
+ }
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
index 430adeb21..d460d9497 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
@@ -31,10 +31,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
private readonly Configuration configuration;
+ private JpegFrame frame;
+
+ private IRawJpegData jpegData;
+
///
/// Jpeg component converters from decompressed spectral to color data.
///
- private JpegComponentPostProcessor[] componentProcessors;
+ private ComponentProcessor[] componentProcessors;
///
/// Color converter from jpeg color space to target pixel color space.
@@ -66,13 +70,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
private int pixelRowCounter;
+ ///
+ /// Represent target size after decoding for scaling decoding mode.
+ ///
+ ///
+ /// Null if no scaling is required.
+ ///
+ private Size? targetSize;
+
///
/// Initializes a new instance of the class.
///
/// The configuration.
- public SpectralConverter(Configuration configuration) =>
+ /// Optional target size for decoded image.
+ public SpectralConverter(Configuration configuration, Size? targetSize = null)
+ {
this.configuration = configuration;
+ this.targetSize = targetSize;
+ }
+
///
/// Gets converted pixel buffer.
///
@@ -86,6 +103,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
if (!this.Converted)
{
+ this.PrepareForDecoding();
+
int steps = (int)Math.Ceiling(this.pixelBuffer.Height / (float)this.pixelRowsPerStep);
for (int step = 0; step < steps; step++)
@@ -95,56 +114,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
- return this.pixelBuffer;
- }
-
- ///
- public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
- {
- MemoryAllocator allocator = this.configuration.MemoryAllocator;
-
- // iteration data
- int majorBlockWidth = frame.Components.Max((component) => component.SizeInBlocks.Width);
- int majorVerticalSamplingFactor = frame.Components.Max((component) => component.SamplingFactors.Height);
-
- const int blockPixelHeight = 8;
- this.pixelRowsPerStep = majorVerticalSamplingFactor * blockPixelHeight;
-
- // pixel buffer for resulting image
- this.pixelBuffer = allocator.Allocate2D(
- frame.PixelWidth,
- frame.PixelHeight,
- this.configuration.PreferContiguousImageBuffers);
- this.paddedProxyPixelRow = allocator.Allocate(frame.PixelWidth + 3);
-
- // component processors from spectral to Rgba32
- const int blockPixelWidth = 8;
- var postProcessorBufferSize = new Size(majorBlockWidth * blockPixelWidth, this.pixelRowsPerStep);
- this.componentProcessors = new JpegComponentPostProcessor[frame.Components.Length];
- for (int i = 0; i < this.componentProcessors.Length; i++)
- {
- this.componentProcessors[i] = new JpegComponentPostProcessor(allocator, frame, jpegData, postProcessorBufferSize, frame.Components[i]);
- }
-
- // single 'stride' rgba32 buffer for conversion between spectral and TPixel
- this.rgbBuffer = allocator.Allocate(frame.PixelWidth * 3);
-
- // color converter from Rgba32 to TPixel
- this.colorConverter = this.GetColorConverter(frame, jpegData);
- }
-
- ///
- public override void ConvertStrideBaseline()
- {
- // Convert next pixel stride using single spectral `stride'
- // Note that zero passing eliminates the need of virtual call
- // from JpegComponentPostProcessor
- this.ConvertStride(spectralStep: 0);
-
- foreach (JpegComponentPostProcessor cpp in this.componentProcessors)
- {
- cpp.ClearSpectralBuffers();
- }
+ Buffer2D buffer = this.pixelBuffer;
+ this.pixelBuffer = null;
+ return buffer;
}
///
@@ -197,12 +169,88 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.pixelRowCounter += this.pixelRowsPerStep;
}
+ ///
+ public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
+ {
+ this.frame = frame;
+ this.jpegData = jpegData;
+ }
+
+ ///
+ public override void PrepareForDecoding()
+ {
+ DebugGuard.IsTrue(this.colorConverter == null, "SpectralConverter.PrepareForDecoding() must be called once.");
+
+ MemoryAllocator allocator = this.configuration.MemoryAllocator;
+
+ // color converter from RGB to TPixel
+ JpegColorConverterBase converter = this.GetColorConverter(this.frame, this.jpegData);
+ this.colorConverter = converter;
+
+ // resulting image size
+ Size pixelSize = CalculateResultingImageSize(this.frame.PixelSize, this.targetSize, out int blockPixelSize);
+
+ // iteration data
+ int majorBlockWidth = this.frame.Components.Max((component) => component.SizeInBlocks.Width);
+ int majorVerticalSamplingFactor = this.frame.Components.Max((component) => component.SamplingFactors.Height);
+
+ this.pixelRowsPerStep = majorVerticalSamplingFactor * blockPixelSize;
+
+ // pixel buffer for resulting image
+ this.pixelBuffer = allocator.Allocate2D(
+ pixelSize.Width,
+ pixelSize.Height,
+ this.configuration.PreferContiguousImageBuffers);
+ this.paddedProxyPixelRow = allocator.Allocate(pixelSize.Width + 3);
+
+ // component processors from spectral to RGB
+ int bufferWidth = majorBlockWidth * blockPixelSize;
+ int batchSize = converter.ElementsPerBatch;
+ int batchRemainder = bufferWidth & (batchSize - 1);
+ var postProcessorBufferSize = new Size(bufferWidth + (batchSize - batchRemainder), this.pixelRowsPerStep);
+ this.componentProcessors = this.CreateComponentProcessors(this.frame, this.jpegData, blockPixelSize, postProcessorBufferSize);
+
+ // single 'stride' rgba32 buffer for conversion between spectral and TPixel
+ this.rgbBuffer = allocator.Allocate(pixelSize.Width * 3);
+ }
+
+ ///
+ public override void ConvertStrideBaseline()
+ {
+ // Convert next pixel stride using single spectral `stride'
+ // Note that zero passing eliminates extra virtual call
+ this.ConvertStride(spectralStep: 0);
+
+ foreach (ComponentProcessor cpp in this.componentProcessors)
+ {
+ cpp.ClearSpectralBuffers();
+ }
+ }
+
+ protected ComponentProcessor[] CreateComponentProcessors(JpegFrame frame, IRawJpegData jpegData, int blockPixelSize, Size processorBufferSize)
+ {
+ MemoryAllocator allocator = this.configuration.MemoryAllocator;
+ var componentProcessors = new ComponentProcessor[frame.Components.Length];
+ for (int i = 0; i < componentProcessors.Length; i++)
+ {
+ componentProcessors[i] = blockPixelSize switch
+ {
+ 4 => new DownScalingComponentProcessor2(allocator, frame, jpegData, processorBufferSize, frame.Components[i]),
+ 2 => new DownScalingComponentProcessor4(allocator, frame, jpegData, processorBufferSize, frame.Components[i]),
+ 1 => new DownScalingComponentProcessor8(allocator, frame, jpegData, processorBufferSize, frame.Components[i]),
+ _ => new DirectComponentProcessor(allocator, frame, jpegData, processorBufferSize, frame.Components[i]),
+ };
+ }
+
+ return componentProcessors;
+ }
+
///
public void Dispose()
{
if (this.componentProcessors != null)
{
- foreach (JpegComponentPostProcessor cpp in this.componentProcessors)
+ foreach (ComponentProcessor cpp in this.componentProcessors)
{
cpp.Dispose();
}
@@ -210,6 +258,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.rgbBuffer?.Dispose();
this.paddedProxyPixelRow?.Dispose();
+ this.pixelBuffer?.Dispose();
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs
index e2416d927..cd769b558 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs
index 44b39dfd7..7bdab29cd 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
index 6acc6b6db..24ab9f8d1 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.IO;
@@ -139,8 +139,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
public void Encode444(Image pixels, ref Block8x8F luminanceQuantTable, ref Block8x8F chrominanceQuantTable, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
- FastFloatingPointDCT.AdjustToFDCT(ref luminanceQuantTable);
- FastFloatingPointDCT.AdjustToFDCT(ref chrominanceQuantTable);
+ FloatingPointDCT.AdjustToFDCT(ref luminanceQuantTable);
+ FloatingPointDCT.AdjustToFDCT(ref chrominanceQuantTable);
this.huffmanTables = HuffmanLut.TheHuffmanLut;
@@ -202,8 +202,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
public void Encode420(Image pixels, ref Block8x8F luminanceQuantTable, ref Block8x8F chrominanceQuantTable, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
- FastFloatingPointDCT.AdjustToFDCT(ref luminanceQuantTable);
- FastFloatingPointDCT.AdjustToFDCT(ref chrominanceQuantTable);
+ FloatingPointDCT.AdjustToFDCT(ref luminanceQuantTable);
+ FloatingPointDCT.AdjustToFDCT(ref chrominanceQuantTable);
this.huffmanTables = HuffmanLut.TheHuffmanLut;
@@ -271,7 +271,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
public void EncodeGrayscale(Image pixels, ref Block8x8F luminanceQuantTable, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
- FastFloatingPointDCT.AdjustToFDCT(ref luminanceQuantTable);
+ FloatingPointDCT.AdjustToFDCT(ref luminanceQuantTable);
this.huffmanTables = HuffmanLut.TheHuffmanLut;
@@ -319,7 +319,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
public void EncodeRgb(Image pixels, ref Block8x8F quantTable, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
- FastFloatingPointDCT.AdjustToFDCT(ref quantTable);
+ FloatingPointDCT.AdjustToFDCT(ref quantTable);
this.huffmanTables = HuffmanLut.TheHuffmanLut;
@@ -391,7 +391,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
block.AddInPlace(-128f);
// Discrete cosine transform
- FastFloatingPointDCT.TransformFDCT(ref block);
+ FloatingPointDCT.TransformFDCT(ref block);
// Quantization
Block8x8F.Quantize(ref block, ref spectralBlock, ref quant);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs
index 51364e3c7..fb04168b5 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs
index 6c402fcfd..29a13d201 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs
@@ -1,9 +1,14 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@@ -74,6 +79,44 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
ref Block8x8F yBlock = ref this.Y;
ref L8 l8Start = ref MemoryMarshal.GetReference(this.l8Span);
+ if (RgbToYCbCrConverterVectorized.IsSupported)
+ {
+ ConvertAvx(ref l8Start, ref yBlock);
+ }
+ else
+ {
+ ConvertScalar(ref l8Start, ref yBlock);
+ }
+ }
+
+ ///
+ /// Converts 8x8 L8 pixel matrix to 8x8 Block of floats using Avx2 Intrinsics.
+ ///
+ /// Start of span of L8 pixels with size of 64
+ /// 8x8 destination matrix of Luminance(Y) converted data
+ private static void ConvertAvx(ref L8 l8Start, ref Block8x8F yBlock)
+ {
+ Debug.Assert(RgbToYCbCrConverterVectorized.IsSupported, "AVX2 is required to run this converter");
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+ ref Vector128 l8ByteSpan = ref Unsafe.As>(ref l8Start);
+ ref Vector256 destRef = ref yBlock.V0;
+
+ const int bytesPerL8Stride = 8;
+ for (nint i = 0; i < 8; i++)
+ {
+ Unsafe.Add(ref destRef, i) = Avx2.ConvertToVector256Single(Avx2.ConvertToVector256Int32(Unsafe.AddByteOffset(ref l8ByteSpan, bytesPerL8Stride * i)));
+ }
+#endif
+ }
+
+ ///
+ /// Converts 8x8 L8 pixel matrix to 8x8 Block of floats.
+ ///
+ /// Start of span of L8 pixels with size of 64
+ /// 8x8 destination matrix of Luminance(Y) converted data
+ private static void ConvertScalar(ref L8 l8Start, ref Block8x8F yBlock)
+ {
for (int i = 0; i < Block8x8F.Size; i++)
{
ref L8 c = ref Unsafe.Add(ref l8Start, i);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs
index f9d0fba57..7edd9da43 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs
index 789277d7d..3c1daadb0 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs
@@ -1,9 +1,14 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@@ -94,10 +99,56 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
ref Block8x8F greenBlock = ref this.G;
ref Block8x8F blueBlock = ref this.B;
- CopyToBlock(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock);
+ if (RgbToYCbCrConverterVectorized.IsSupported)
+ {
+ ConvertAvx(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock);
+ }
+ else
+ {
+ ConvertScalar(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock);
+ }
+ }
+
+ ///
+ /// Converts 8x8 RGB24 pixel matrix to 8x8 Block of floats using Avx2 Intrinsics.
+ ///
+ /// Span of Rgb24 pixels with size of 64
+ /// 8x8 destination matrix of Red converted data
+ /// 8x8 destination matrix of Blue converted data
+ /// 8x8 destination matrix of Green converted data
+ private static void ConvertAvx(Span rgbSpan, ref Block8x8F rBlock, ref Block8x8F gBlock, ref Block8x8F bBlock)
+ {
+ Debug.Assert(RgbToYCbCrConverterVectorized.IsSupported, "AVX2 is required to run this converter");
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+ ref Vector256 rgbByteSpan = ref Unsafe.As>(ref MemoryMarshal.GetReference(rgbSpan));
+ ref Vector256 redRef = ref rBlock.V0;
+ ref Vector256 greenRef = ref gBlock.V0;
+ ref Vector256 blueRef = ref bBlock.V0;
+ var zero = Vector256.Create(0).AsByte();
+
+ var extractToLanesMask = Unsafe.As>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.MoveFirst24BytesToSeparateLanes));
+ var extractRgbMask = Unsafe.As>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.ExtractRgb));
+ Vector256 rgb, rg, bx;
+
+ const int bytesPerRgbStride = 24;
+ for (nint i = 0; i < 8; i++)
+ {
+ rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, bytesPerRgbStride * i).AsUInt32(), extractToLanesMask).AsByte();
+
+ rgb = Avx2.Shuffle(rgb, extractRgbMask);
+
+ rg = Avx2.UnpackLow(rgb, zero);
+ bx = Avx2.UnpackHigh(rgb, zero);
+
+ Unsafe.Add(ref redRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32());
+ Unsafe.Add(ref greenRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32());
+ Unsafe.Add(ref blueRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32());
+ }
+#endif
}
- private static void CopyToBlock(Span rgbSpan, ref Block8x8F redBlock, ref Block8x8F greenBlock, ref Block8x8F blueBlock)
+ private static void ConvertScalar(Span rgbSpan, ref Block8x8F redBlock, ref Block8x8F greenBlock, ref Block8x8F blueBlock)
{
ref Rgb24 rgbStart = ref MemoryMarshal.GetReference(rgbSpan);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterLut.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterLut.cs
index 15574a32a..c3fc806a7 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterLut.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterLut.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs
index 9566ee862..c2bc8cfb8 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Diagnostics;
@@ -60,13 +60,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
#if SUPPORTS_RUNTIME_INTRINSICS
- private static ReadOnlySpan MoveFirst24BytesToSeparateLanes => new byte[]
+ internal static ReadOnlySpan MoveFirst24BytesToSeparateLanes => new byte[]
{
0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0,
3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0
};
- private static ReadOnlySpan ExtractRgb => new byte[]
+ internal static ReadOnlySpan ExtractRgb => new byte[]
{
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF,
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs
index 3a878f3c6..ddf3c5292 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.InteropServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs
index 5f7725bdd..96a5569da 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter444{TPixel}.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.InteropServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
index 6d3620c62..48aacd7ff 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs
similarity index 81%
rename from src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.Intrinsic.cs
rename to src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs
index 8acc4b626..19349e454 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.Intrinsic.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
@@ -7,20 +7,8 @@ using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
- internal static partial class FastFloatingPointDCT
+ internal static partial class FloatingPointDCT
{
-#pragma warning disable SA1310, SA1311, IDE1006 // naming rule violation warnings
- private static readonly Vector256 mm256_F_0_7071 = Vector256.Create(0.707106781f);
- private static readonly Vector256 mm256_F_0_3826 = Vector256.Create(0.382683433f);
- private static readonly Vector256 mm256_F_0_5411 = Vector256.Create(0.541196100f);
- private static readonly Vector256 mm256_F_1_3065 = Vector256.Create(1.306562965f);
-
- private static readonly Vector256 mm256_F_1_4142 = Vector256.Create(1.414213562f);
- private static readonly Vector256 mm256_F_1_8477 = Vector256.Create(1.847759065f);
- private static readonly Vector256 mm256_F_n1_0823 = Vector256.Create(-1.082392200f);
- private static readonly Vector256 mm256_F_n2_6131 = Vector256.Create(-2.613125930f);
-#pragma warning restore SA1310, SA1311, IDE1006
-
///
/// Apply floating point FDCT inplace using simd operations.
///
@@ -57,6 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
block.V0 = Avx.Add(tmp10, tmp11);
block.V4 = Avx.Subtract(tmp10, tmp11);
+ Vector256 mm256_F_0_7071 = Vector256.Create(0.707106781f);
Vector256 z1 = Avx.Multiply(Avx.Add(tmp12, tmp13), mm256_F_0_7071);
block.V2 = Avx.Add(tmp13, z1);
block.V6 = Avx.Subtract(tmp13, z1);
@@ -66,9 +55,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
tmp11 = Avx.Add(tmp5, tmp6);
tmp12 = Avx.Add(tmp6, tmp7);
- Vector256 z5 = Avx.Multiply(Avx.Subtract(tmp10, tmp12), mm256_F_0_3826);
- Vector256 z2 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, mm256_F_0_5411, tmp10);
- Vector256 z4 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, mm256_F_1_3065, tmp12);
+ Vector256 z5 = Avx.Multiply(Avx.Subtract(tmp10, tmp12), Vector256.Create(0.382683433f)); // mm256_F_0_3826
+ Vector256 z2 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, Vector256.Create(0.541196100f), tmp10); // mm256_F_0_5411
+ Vector256 z4 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, Vector256.Create(1.306562965f), tmp12); // mm256_F_1_3065
Vector256 z3 = Avx.Multiply(tmp11, mm256_F_0_7071);
Vector256 z11 = Avx.Add(tmp7, z3);
@@ -109,6 +98,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
Vector256 tmp10 = Avx.Add(z5, tmp2);
Vector256 tmp11 = Avx.Subtract(z5, tmp2);
+ Vector256 mm256_F_1_4142 = Vector256.Create(1.414213562f);
Vector256 tmp13 = Avx.Add(tmp1, tmp3);
Vector256 tmp12 = SimdUtils.HwIntrinsics.MultiplySubstract(tmp13, Avx.Subtract(tmp1, tmp3), mm256_F_1_4142);
@@ -131,10 +121,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
tmp7 = Avx.Add(z11, z13);
tmp11 = Avx.Multiply(Avx.Subtract(z11, z13), mm256_F_1_4142);
- z5 = Avx.Multiply(Avx.Add(z10, z12), mm256_F_1_8477);
+ z5 = Avx.Multiply(Avx.Add(z10, z12), Vector256.Create(1.847759065f)); // mm256_F_1_8477
- tmp10 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, z12, mm256_F_n1_0823);
- tmp12 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, z10, mm256_F_n2_6131);
+ tmp10 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, z12, Vector256.Create(-1.082392200f)); // mm256_F_n1_0823
+ tmp12 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, z10, Vector256.Create(-2.613125930f)); // mm256_F_n2_6131
tmp6 = Avx.Subtract(tmp12, tmp7);
tmp5 = Avx.Subtract(tmp11, tmp6);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs b/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.cs
similarity index 93%
rename from src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs
rename to src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.cs
index e1bcff30f..37ec6e190 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -12,9 +12,12 @@ using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
///
- /// Contains inaccurate, but fast forward and inverse DCT implementations.
+ /// Contains floating point forward and inverse DCT implementations
///
- internal static partial class FastFloatingPointDCT
+ ///
+ /// Based on "Arai, Agui and Nakajima" algorithm.
+ ///
+ internal static partial class FloatingPointDCT
{
#pragma warning disable SA1310, SA1311, IDE1006 // naming rules violation warnings
private static readonly Vector4 mm128_F_0_7071 = new(0.707106781f);
@@ -70,8 +73,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
ref float multipliersRef = ref MemoryMarshal.GetReference(AdjustmentCoefficients);
for (nint i = 0; i < Block8x8F.Size; i++)
{
- tableRef = 0.125f * tableRef * Unsafe.Add(ref multipliersRef, i);
- tableRef = ref Unsafe.Add(ref tableRef, 1);
+ ref float elemRef = ref Unsafe.Add(ref tableRef, i);
+ elemRef = 0.125f * elemRef * Unsafe.Add(ref multipliersRef, i);
}
// Spectral macroblocks are transposed before quantization
@@ -89,8 +92,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
ref float multipliersRef = ref MemoryMarshal.GetReference(AdjustmentCoefficients);
for (nint i = 0; i < Block8x8F.Size; i++)
{
- tableRef = 0.125f / (tableRef * Unsafe.Add(ref multipliersRef, i));
- tableRef = ref Unsafe.Add(ref tableRef, 1);
+ ref float elemRef = ref Unsafe.Add(ref tableRef, i);
+ elemRef = 0.125f / (elemRef * Unsafe.Add(ref multipliersRef, i));
}
// Spectral macroblocks are not transposed before quantization
@@ -103,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Apply 2D floating point IDCT inplace.
///
///
- /// Input block must be dequantized before this method with table
+ /// Input block must be dequantized with quantization table
/// adjusted by .
///
/// Input block.
@@ -125,8 +128,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Apply 2D floating point IDCT inplace.
///
///
- /// Input block must be quantized after this method with table adjusted
- /// by .
+ /// Input block must be quantized after this method with quantization
+ /// table adjusted by .
///
/// Input block.
public static void TransformFDCT(ref Block8x8F block)
@@ -221,7 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Apply floating point FDCT inplace using API.
///
/// Input block.
- public static void FDCT_Vector4(ref Block8x8F block)
+ private static void FDCT_Vector4(ref Block8x8F block)
{
// First pass - process columns
FDCT8x4_Vector4(ref block.V0L);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs b/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs
index eab5e6a08..fa3fede33 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs b/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
index d4a4c1cf4..c899cf3ad 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ScaledFloatingPointDCT.cs b/src/ImageSharp/Formats/Jpeg/Components/ScaledFloatingPointDCT.cs
new file mode 100644
index 000000000..037995ea1
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/ScaledFloatingPointDCT.cs
@@ -0,0 +1,220 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+using System.Runtime.CompilerServices;
+
+#pragma warning disable IDE0078
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
+{
+ ///
+ /// Contains floating point forward DCT implementations with built-in scaling.
+ ///
+ ///
+ /// Based on "Loeffler, Ligtenberg, and Moschytz" algorithm.
+ ///
+ internal static class ScaledFloatingPointDCT
+ {
+#pragma warning disable SA1310
+ private const float FP32_0_541196100 = 0.541196100f;
+ private const float FP32_0_765366865 = 0.765366865f;
+ private const float FP32_1_847759065 = 1.847759065f;
+ private const float FP32_0_211164243 = 0.211164243f;
+ private const float FP32_1_451774981 = 1.451774981f;
+ private const float FP32_2_172734803 = 2.172734803f;
+ private const float FP32_1_061594337 = 1.061594337f;
+ private const float FP32_0_509795579 = 0.509795579f;
+ private const float FP32_0_601344887 = 0.601344887f;
+ private const float FP32_0_899976223 = 0.899976223f;
+ private const float FP32_2_562915447 = 2.562915447f;
+ private const float FP32_0_720959822 = 0.720959822f;
+ private const float FP32_0_850430095 = 0.850430095f;
+ private const float FP32_1_272758580 = 1.272758580f;
+ private const float FP32_3_624509785 = 3.624509785f;
+#pragma warning restore SA1310
+
+ ///
+ /// Adjusts given quantization table for usage with IDCT algorithms
+ /// from .
+ ///
+ /// Quantization table to adjust.
+ public static void AdjustToIDCT(ref Block8x8F quantTable)
+ {
+ ref float tableRef = ref Unsafe.As(ref quantTable);
+ for (nint i = 0; i < Block8x8F.Size; i++)
+ {
+ ref float elemRef = ref Unsafe.Add(ref tableRef, i);
+ elemRef = 0.125f * elemRef;
+ }
+
+ // Spectral macroblocks are transposed before quantization
+ // so we must transpose quantization table
+ quantTable.TransposeInplace();
+ }
+
+ ///
+ /// Apply 2D floating point 'donwscaling' IDCT inplace producing
+ /// 8x8 -> 4x4 result.
+ ///
+ ///
+ /// Resulting matrix is stored in the top left 4x4 part of the
+ /// .
+ ///
+ /// Input block.
+ /// Dequantization table adjusted by .
+ /// Output range normalization value, 1/2 of the .
+ /// Maximum value of the output range.
+ public static void TransformIDCT_4x4(ref Block8x8F block, ref Block8x8F dequantTable, float normalizationValue, float maxValue)
+ {
+ for (int ctr = 0; ctr < 8; ctr++)
+ {
+ // Don't process row 4, second pass doesn't use it
+ if (ctr == 4)
+ {
+ continue;
+ }
+
+ // Even part
+ float tmp0 = block[(ctr * 8) + 0] * dequantTable[(ctr * 8) + 0] * 2;
+
+ float z2 = block[(ctr * 8) + 2] * dequantTable[(ctr * 8) + 2];
+ float z3 = block[(ctr * 8) + 6] * dequantTable[(ctr * 8) + 6];
+
+ float tmp2 = (z2 * FP32_1_847759065) + (z3 * -FP32_0_765366865);
+
+ float tmp10 = tmp0 + tmp2;
+ float tmp12 = tmp0 - tmp2;
+
+ // Odd part
+ float z1 = block[(ctr * 8) + 7] * dequantTable[(ctr * 8) + 7];
+ z2 = block[(ctr * 8) + 5] * dequantTable[(ctr * 8) + 5];
+ z3 = block[(ctr * 8) + 3] * dequantTable[(ctr * 8) + 3];
+ float z4 = block[(ctr * 8) + 1] * dequantTable[(ctr * 8) + 1];
+
+ tmp0 = (z1 * -FP32_0_211164243) +
+ (z2 * FP32_1_451774981) +
+ (z3 * -FP32_2_172734803) +
+ (z4 * FP32_1_061594337);
+
+ tmp2 = (z1 * -FP32_0_509795579) +
+ (z2 * -FP32_0_601344887) +
+ (z3 * FP32_0_899976223) +
+ (z4 * FP32_2_562915447);
+
+ // temporal result is saved to +4 shifted indices
+ // because result is saved into the top left 2x2 region of the
+ // input block
+ block[(ctr * 8) + 0 + 4] = (tmp10 + tmp2) / 2;
+ block[(ctr * 8) + 3 + 4] = (tmp10 - tmp2) / 2;
+ block[(ctr * 8) + 1 + 4] = (tmp12 + tmp0) / 2;
+ block[(ctr * 8) + 2 + 4] = (tmp12 - tmp0) / 2;
+ }
+
+ for (int ctr = 0; ctr < 4; ctr++)
+ {
+ // Even part
+ float tmp0 = block[ctr + (8 * 0) + 4] * 2;
+
+ float tmp2 = (block[ctr + (8 * 2) + 4] * FP32_1_847759065) + (block[ctr + (8 * 6) + 4] * -FP32_0_765366865);
+
+ float tmp10 = tmp0 + tmp2;
+ float tmp12 = tmp0 - tmp2;
+
+ // Odd part
+ float z1 = block[ctr + (8 * 7) + 4];
+ float z2 = block[ctr + (8 * 5) + 4];
+ float z3 = block[ctr + (8 * 3) + 4];
+ float z4 = block[ctr + (8 * 1) + 4];
+
+ tmp0 = (z1 * -FP32_0_211164243) +
+ (z2 * FP32_1_451774981) +
+ (z3 * -FP32_2_172734803) +
+ (z4 * FP32_1_061594337);
+
+ tmp2 = (z1 * -FP32_0_509795579) +
+ (z2 * -FP32_0_601344887) +
+ (z3 * FP32_0_899976223) +
+ (z4 * FP32_2_562915447);
+
+ // Save results to the top left 4x4 subregion
+ block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(((tmp10 + tmp2) / 2) + normalizationValue, 0, maxValue));
+ block[(ctr * 8) + 3] = MathF.Round(Numerics.Clamp(((tmp10 - tmp2) / 2) + normalizationValue, 0, maxValue));
+ block[(ctr * 8) + 1] = MathF.Round(Numerics.Clamp(((tmp12 + tmp0) / 2) + normalizationValue, 0, maxValue));
+ block[(ctr * 8) + 2] = MathF.Round(Numerics.Clamp(((tmp12 - tmp0) / 2) + normalizationValue, 0, maxValue));
+ }
+ }
+
+ ///
+ /// Apply 2D floating point 'donwscaling' IDCT inplace producing
+ /// 8x8 -> 2x2 result.
+ ///
+ ///
+ /// Resulting matrix is stored in the top left 2x2 part of the
+ /// .
+ ///
+ /// Input block.
+ /// Dequantization table adjusted by .
+ /// Output range normalization value, 1/2 of the .
+ /// Maximum value of the output range.
+ public static void TransformIDCT_2x2(ref Block8x8F block, ref Block8x8F dequantTable, float normalizationValue, float maxValue)
+ {
+ for (int ctr = 0; ctr < 8; ctr++)
+ {
+ // Don't process rows 2/4/6, second pass doesn't use it
+ if (ctr == 2 || ctr == 4 || ctr == 6)
+ {
+ continue;
+ }
+
+ // Even part
+ float tmp0;
+ float z1 = block[(ctr * 8) + 0] * dequantTable[(ctr * 8) + 0];
+ float tmp10 = z1 * 4;
+
+ // Odd part
+ z1 = block[(ctr * 8) + 7] * dequantTable[(ctr * 8) + 7];
+ tmp0 = z1 * -FP32_0_720959822;
+ z1 = block[(ctr * 8) + 5] * dequantTable[(ctr * 8) + 5];
+ tmp0 += z1 * FP32_0_850430095;
+ z1 = block[(ctr * 8) + 3] * dequantTable[(ctr * 8) + 3];
+ tmp0 += z1 * -FP32_1_272758580;
+ z1 = block[(ctr * 8) + 1] * dequantTable[(ctr * 8) + 1];
+ tmp0 += z1 * FP32_3_624509785;
+
+ // temporal result is saved to +2 shifted indices
+ // because result is saved into the top left 2x2 region of the
+ // input block
+ block[(ctr * 8) + 2] = (tmp10 + tmp0) / 4;
+ block[(ctr * 8) + 3] = (tmp10 - tmp0) / 4;
+ }
+
+ for (int ctr = 0; ctr < 2; ctr++)
+ {
+ // Even part
+ float tmp10 = block[ctr + (8 * 0) + 2] * 4;
+
+ // Odd part
+ float tmp0 = (block[ctr + (8 * 7) + 2] * -FP32_0_720959822) +
+ (block[ctr + (8 * 5) + 2] * FP32_0_850430095) +
+ (block[ctr + (8 * 3) + 2] * -FP32_1_272758580) +
+ (block[ctr + (8 * 1) + 2] * FP32_3_624509785);
+
+ // Save results to the top left 2x2 subregion
+ block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(((tmp10 + tmp0) / 4) + normalizationValue, 0, maxValue));
+ block[(ctr * 8) + 1] = MathF.Round(Numerics.Clamp(((tmp10 - tmp0) / 4) + normalizationValue, 0, maxValue));
+ }
+ }
+
+ ///
+ /// Apply 2D floating point 'donwscaling' IDCT inplace producing
+ /// 8x8 -> 1x1 result.
+ ///
+ /// Direct current term value from input block.
+ /// Dequantization value.
+ /// Output range normalization value, 1/2 of the .
+ /// Maximum value of the output range.
+ public static float TransformIDCT_1x1(float dc, float dequantizer, float normalizationValue, float maxValue)
+ => MathF.Round(Numerics.Clamp((dc * dequantizer) + normalizationValue, 0, maxValue));
+ }
+}
+#pragma warning restore IDE0078
diff --git a/src/ImageSharp/Formats/Jpeg/Components/SizeExtensions.cs b/src/ImageSharp/Formats/Jpeg/Components/SizeExtensions.cs
index 12d21648f..fa7778cba 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/SizeExtensions.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/SizeExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.Intrinsic.cs
index 850de26c3..ee1ec501b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.Intrinsic.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.Intrinsic.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
index ab80b3ae6..c18333a92 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs b/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs
index 0b49ddfe6..76874b7ff 100644
--- a/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs
+++ b/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg
{
diff --git a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs b/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs
index 70cfd18e9..d14207ef0 100644
--- a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg
{
diff --git a/src/ImageSharp/Formats/Jpeg/JpegColorType.cs b/src/ImageSharp/Formats/Jpeg/JpegColorType.cs
index c15038c23..f4c54819a 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegColorType.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegColorType.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg
{
diff --git a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs
index 699eb95a3..e98f146a2 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jpeg
{
diff --git a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs
index 89c4de550..a4ba8725e 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
@@ -306,17 +306,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public const int RegisterSize = 64;
///
- /// The number of bits to fetch when filling the buffer.
+ /// The number of bits to fetch when filling the buffer.
///
public const int FetchBits = 48;
///
- /// The number of times to read the input stream when filling the buffer.
+ /// The number of times to read the input stream when filling the buffer.
///
public const int FetchLoop = FetchBits / 8;
///
- /// The minimum number of bits allowed before by the before fetching.
+ /// The minimum number of bits allowed before by the before fetching.
///
public const int MinBits = RegisterSize - FetchBits;
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
index 18212ffc7..7f23d1ac8 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
@@ -1,9 +1,10 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
+using SixLabors.ImageSharp.IO;
+using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg
@@ -17,56 +18,62 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public bool IgnoreMetadata { get; set; }
///
- public Image Decode(Configuration configuration, Stream stream)
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(stream, nameof(stream));
using var decoder = new JpegDecoderCore(configuration, this);
- return decoder.Decode(configuration, stream);
+ return decoder.Decode(configuration, stream, cancellationToken);
}
///
- public Image Decode(Configuration configuration, Stream stream)
- => this.Decode(configuration, stream);
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
+ => this.Decode(configuration, stream, cancellationToken);
- ///
- public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
+ ///
+ /// Placeholder summary.
+ ///
+ /// Placeholder2
+ /// Placeholder3
+ /// Placeholder4
+ /// Placeholder5
+ /// Placeholder6
+ internal Image DecodeInto(Configuration configuration, Stream stream, Size targetSize, CancellationToken cancellationToken)
+ => this.DecodeInto(configuration, stream, targetSize, cancellationToken);
+
+ ///
+ /// Decodes and downscales the image from the specified stream if possible.
+ ///
+ /// The pixel format.
+ /// Configuration.
+ /// Stream.
+ /// Target size.
+ /// Cancellation token.
+ internal Image DecodeInto(Configuration configuration, Stream stream, Size targetSize, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(stream, nameof(stream));
using var decoder = new JpegDecoderCore(configuration, this);
- return decoder.DecodeAsync(configuration, stream, cancellationToken);
+ using var bufferedReadStream = new BufferedReadStream(configuration, stream);
+ try
+ {
+ return decoder.DecodeInto(bufferedReadStream, targetSize, cancellationToken);
+ }
+ catch (InvalidMemoryOperationException ex)
+ {
+ throw new InvalidImageContentException(((IImageDecoderInternals)decoder).Dimensions, ex);
+ }
}
- ///
- public async Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- => await this.DecodeAsync(configuration, stream, cancellationToken)
- .ConfigureAwait(false);
-
///
- public IImageInfo Identify(Configuration configuration, Stream stream)
+ public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(stream, nameof(stream));
using var decoder = new JpegDecoderCore(configuration, this);
- return decoder.Identify(configuration, stream);
- }
-
- ///
- public async Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- {
- Guard.NotNull(stream, nameof(stream));
-
- // The introduction of a local variable that refers to an object the implements
- // IDisposable means you must use async/await, where the compiler generates the
- // state machine and a continuation.
- using (var decoder = new JpegDecoderCore(configuration, this))
- {
- return await decoder.IdentifyAsync(configuration, stream, cancellationToken)
- .ConfigureAwait(false);
- }
+ return decoder.Identify(configuration, stream, cancellationToken);
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index 023928f37..11187fb91 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -1,9 +1,10 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
using System.Buffers.Binary;
+using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -84,6 +85,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
private byte[] xmpData;
+ ///
+ /// Whether the image has a APP14 adobe marker. This is needed to determine image encoded colorspace.
+ ///
+ private bool hasAdobeMarker;
+
///
/// Contains information about the JFIF marker.
///
@@ -97,7 +103,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Scan decoder.
///
- private HuffmanScanDecoder scanDecoder;
+ private IJpegScanDecoder scanDecoder;
+
+ ///
+ /// The arithmetic decoding tables.
+ ///
+ private List arithmeticDecodingTables;
+
+ ///
+ /// The restart interval.
+ ///
+ private int? resetInterval;
///
/// Initializes a new instance of the class.
@@ -140,7 +156,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public JpegComponent[] Components => this.Frame.Components;
///
- IJpegComponent[] IRawJpegData.Components => this.Components;
+ JpegComponent[] IRawJpegData.Components => this.Components;
///
public Block8x8F[] QuantizationTables { get; private set; }
@@ -148,73 +164,85 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Finds the next file marker within the byte stream.
///
- /// The buffer to read file markers to.
/// The input stream.
- /// The
- public static JpegFileMarker FindNextFileMarker(byte[] marker, BufferedReadStream stream)
+ /// The .
+ public static JpegFileMarker FindNextFileMarker(BufferedReadStream stream)
{
- int value = stream.Read(marker, 0, 2);
-
- if (value == 0)
+ while (true)
{
- return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2);
- }
+ int b = stream.ReadByte();
+ if (b == -1)
+ {
+ return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2);
+ }
- if (marker[0] == JpegConstants.Markers.XFF)
- {
- // According to Section B.1.1.2:
- // "Any marker may optionally be preceded by any number of fill bytes, which are bytes assigned code 0xFF."
- int m = marker[1];
- while (m == JpegConstants.Markers.XFF)
+ // Found a marker.
+ if (b == JpegConstants.Markers.XFF)
{
- int suffix = stream.ReadByte();
- if (suffix == -1)
+ while (b == JpegConstants.Markers.XFF)
{
- return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2);
+ // Loop here to discard any padding FF bytes on terminating marker.
+ b = stream.ReadByte();
+ if (b == -1)
+ {
+ return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2);
+ }
}
- m = suffix;
+ // Found a valid marker. Exit loop
+ if (b is not 0 and (< JpegConstants.Markers.RST0 or > JpegConstants.Markers.RST7))
+ {
+ return new JpegFileMarker((byte)(uint)b, stream.Position - 2);
+ }
}
-
- return new JpegFileMarker((byte)m, stream.Position - 2);
}
-
- return new JpegFileMarker(marker[1], stream.Position - 2, true);
}
///
public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
- {
- using var spectralConverter = new SpectralConverter(this.Configuration);
-
- var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, cancellationToken);
+ => this.Decode(stream, targetSize: null, cancellationToken);
- this.ParseStream(stream, scanDecoder, cancellationToken);
+ ///
+ public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
+ {
+ this.ParseStream(stream, spectralConverter: null, cancellationToken);
this.InitExifProfile();
this.InitIccProfile();
this.InitIptcProfile();
this.InitXmpProfile();
this.InitDerivedMetadataProperties();
- return new Image(
- this.Configuration,
- spectralConverter.GetPixelBuffer(cancellationToken),
- this.Metadata);
+ Size pixelSize = this.Frame.PixelSize;
+ return new ImageInfo(new PixelTypeInfo(this.Frame.BitsPerPixel), pixelSize.Width, pixelSize.Height, this.Metadata);
}
- ///
- public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
+ ///
+ /// Decodes and downscales the image from the specified stream if possible.
+ ///
+ /// The pixel format.
+ /// Stream.
+ /// Target size.
+ /// Cancellation token.
+ internal Image DecodeInto(BufferedReadStream stream, Size targetSize, CancellationToken cancellationToken)
+ where TPixel : unmanaged, IPixel
+ => this.Decode(stream, targetSize, cancellationToken);
+
+ private Image Decode(BufferedReadStream stream, Size? targetSize, CancellationToken cancellationToken)
+ where TPixel : unmanaged, IPixel
{
- this.ParseStream(stream, scanDecoder: null, cancellationToken);
+ using var spectralConverter = new SpectralConverter(this.Configuration, targetSize);
+ this.ParseStream(stream, spectralConverter, cancellationToken);
this.InitExifProfile();
this.InitIccProfile();
this.InitIptcProfile();
this.InitXmpProfile();
this.InitDerivedMetadataProperties();
- Size pixelSize = this.Frame.PixelSize;
- return new ImageInfo(new PixelTypeInfo(this.Frame.BitsPerPixel), pixelSize.Width, pixelSize.Height, this.Metadata);
+ return new Image(
+ this.Configuration,
+ spectralConverter.GetPixelBuffer(cancellationToken),
+ this.Metadata);
}
///
@@ -222,17 +250,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// so those tables do not need to be duplicated with segmented tiff's (tiff's with multiple strips).
///
/// The table bytes.
- /// The scan decoder.
- public void LoadTables(byte[] tableBytes, HuffmanScanDecoder huffmanScanDecoder)
+ /// The scan decoder.
+ public void LoadTables(byte[] tableBytes, IJpegScanDecoder scanDecoder)
{
this.Metadata = new ImageMetadata();
this.QuantizationTables = new Block8x8F[4];
- this.scanDecoder = huffmanScanDecoder;
+ this.scanDecoder = scanDecoder;
+ if (tableBytes.Length < 4)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker");
+ }
+
using var ms = new MemoryStream(tableBytes);
using var stream = new BufferedReadStream(this.Configuration, ms);
// Check for the Start Of Image marker.
- stream.Read(this.markerBuffer, 0, 2);
+ int bytesRead = stream.Read(this.markerBuffer, 0, 2);
var fileMarker = new JpegFileMarker(this.markerBuffer[1], 0);
if (fileMarker.Marker != JpegConstants.Markers.SOI)
{
@@ -240,16 +273,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
// Read next marker.
- stream.Read(this.markerBuffer, 0, 2);
- byte marker = this.markerBuffer[1];
- fileMarker = new JpegFileMarker(marker, (int)stream.Position - 2);
+ bytesRead = stream.Read(this.markerBuffer, 0, 2);
+ fileMarker = new JpegFileMarker(this.markerBuffer[1], (int)stream.Position - 2);
while (fileMarker.Marker != JpegConstants.Markers.EOI || (fileMarker.Marker == JpegConstants.Markers.EOI && fileMarker.Invalid))
{
if (!fileMarker.Invalid)
{
// Get the marker length.
- int remaining = this.ReadUint16(stream) - 2;
+ int markerContentByteSize = this.ReadUint16(stream) - 2;
+
+ // Check whether stream actually has enought bytes to read
+ // markerContentByteSize is always positive so we cast
+ // to uint to avoid sign extension
+ if (stream.RemainingBytes < (uint)markerContentByteSize)
+ {
+ JpegThrowHelper.ThrowNotEnoughBytesForMarker(fileMarker.Marker);
+ }
switch (fileMarker.Marker)
{
@@ -259,13 +299,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
case JpegConstants.Markers.RST7:
break;
case JpegConstants.Markers.DHT:
- this.ProcessDefineHuffmanTablesMarker(stream, remaining);
+ this.ProcessDefineHuffmanTablesMarker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.DQT:
- this.ProcessDefineQuantizationTablesMarker(stream, remaining);
+ this.ProcessDefineQuantizationTablesMarker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.DRI:
- this.ProcessDefineRestartIntervalMarker(stream, remaining);
+ this.ProcessDefineRestartIntervalMarker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.EOI:
return;
@@ -273,7 +313,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
// Read next marker.
- stream.Read(this.markerBuffer, 0, 2);
+ bytesRead = stream.Read(this.markerBuffer, 0, 2);
+ if (bytesRead != 2)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker");
+ }
+
fileMarker = new JpegFileMarker(this.markerBuffer[1], 0);
}
}
@@ -282,13 +327,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// Parses the input stream for file markers.
///
/// The input stream.
- /// Scan decoder used exclusively to decode SOS marker.
+ /// The spectral converter to use.
/// The token to monitor cancellation.
- internal void ParseStream(BufferedReadStream stream, HuffmanScanDecoder scanDecoder, CancellationToken cancellationToken)
+ internal void ParseStream(BufferedReadStream stream, SpectralConverter spectralConverter, CancellationToken cancellationToken)
{
- bool metadataOnly = scanDecoder == null;
+ bool metadataOnly = spectralConverter == null;
- this.scanDecoder = scanDecoder;
+ this.scanDecoder ??= new HuffmanScanDecoder(stream, spectralConverter, cancellationToken);
this.Metadata = new ImageMetadata();
@@ -300,29 +345,48 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker.");
}
- stream.Read(this.markerBuffer, 0, 2);
- byte marker = this.markerBuffer[1];
- fileMarker = new JpegFileMarker(marker, (int)stream.Position - 2);
+ fileMarker = FindNextFileMarker(stream);
this.QuantizationTables ??= new Block8x8F[4];
// Break only when we discover a valid EOI marker.
// https://github.com/SixLabors/ImageSharp/issues/695
- while (fileMarker.Marker != JpegConstants.Markers.EOI
- || (fileMarker.Marker == JpegConstants.Markers.EOI && fileMarker.Invalid))
+ while (fileMarker.Marker != JpegConstants.Markers.EOI)
{
cancellationToken.ThrowIfCancellationRequested();
if (!fileMarker.Invalid)
{
// Get the marker length.
- int remaining = this.ReadUint16(stream) - 2;
+ int markerContentByteSize = this.ReadUint16(stream) - 2;
+
+ // Check whether stream actually has enough bytes to read
+ // markerContentByteSize is always positive so we cast
+ // to uint to avoid sign extension.
+ if (stream.RemainingBytes < (uint)markerContentByteSize)
+ {
+ JpegThrowHelper.ThrowNotEnoughBytesForMarker(fileMarker.Marker);
+ }
switch (fileMarker.Marker)
{
case JpegConstants.Markers.SOF0:
case JpegConstants.Markers.SOF1:
case JpegConstants.Markers.SOF2:
- this.ProcessStartOfFrameMarker(stream, remaining, fileMarker, metadataOnly);
+
+ this.ProcessStartOfFrameMarker(stream, markerContentByteSize, fileMarker, ComponentType.Huffman, metadataOnly);
+ break;
+
+ case JpegConstants.Markers.SOF9:
+ case JpegConstants.Markers.SOF10:
+ case JpegConstants.Markers.SOF13:
+ case JpegConstants.Markers.SOF14:
+ this.scanDecoder = new ArithmeticScanDecoder(stream, spectralConverter, cancellationToken);
+ if (this.resetInterval.HasValue)
+ {
+ this.scanDecoder.ResetInterval = this.resetInterval.Value;
+ }
+
+ this.ProcessStartOfFrameMarker(stream, markerContentByteSize, fileMarker, ComponentType.Arithmetic, metadataOnly);
break;
case JpegConstants.Markers.SOF5:
@@ -338,19 +402,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowNotSupportedException("Decoding lossless jpeg files is not supported.");
break;
- case JpegConstants.Markers.SOF9:
- case JpegConstants.Markers.SOF10:
case JpegConstants.Markers.SOF11:
- case JpegConstants.Markers.SOF13:
- case JpegConstants.Markers.SOF14:
case JpegConstants.Markers.SOF15:
- JpegThrowHelper.ThrowNotSupportedException("Decoding jpeg files with arithmetic coding is not supported.");
+ JpegThrowHelper.ThrowNotSupportedException("Decoding jpeg files with lossless arithmetic coding is not supported.");
break;
case JpegConstants.Markers.SOS:
if (!metadataOnly)
{
- this.ProcessStartOfScanMarker(stream, remaining);
+ this.ProcessStartOfScanMarker(stream, markerContentByteSize);
break;
}
else
@@ -364,41 +424,41 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (metadataOnly)
{
- stream.Skip(remaining);
+ stream.Skip(markerContentByteSize);
}
else
{
- this.ProcessDefineHuffmanTablesMarker(stream, remaining);
+ this.ProcessDefineHuffmanTablesMarker(stream, markerContentByteSize);
}
break;
case JpegConstants.Markers.DQT:
- this.ProcessDefineQuantizationTablesMarker(stream, remaining);
+ this.ProcessDefineQuantizationTablesMarker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.DRI:
if (metadataOnly)
{
- stream.Skip(remaining);
+ stream.Skip(markerContentByteSize);
}
else
{
- this.ProcessDefineRestartIntervalMarker(stream, remaining);
+ this.ProcessDefineRestartIntervalMarker(stream, markerContentByteSize);
}
break;
case JpegConstants.Markers.APP0:
- this.ProcessApplicationHeaderMarker(stream, remaining);
+ this.ProcessApplicationHeaderMarker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.APP1:
- this.ProcessApp1Marker(stream, remaining);
+ this.ProcessApp1Marker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.APP2:
- this.ProcessApp2Marker(stream, remaining);
+ this.ProcessApp2Marker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.APP3:
@@ -411,30 +471,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
case JpegConstants.Markers.APP10:
case JpegConstants.Markers.APP11:
case JpegConstants.Markers.APP12:
- stream.Skip(remaining);
+ stream.Skip(markerContentByteSize);
break;
case JpegConstants.Markers.APP13:
- this.ProcessApp13Marker(stream, remaining);
+ this.ProcessApp13Marker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.APP14:
- this.ProcessApp14Marker(stream, remaining);
+ this.ProcessApp14Marker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.APP15:
case JpegConstants.Markers.COM:
- stream.Skip(remaining);
+ stream.Skip(markerContentByteSize);
break;
case JpegConstants.Markers.DAC:
- JpegThrowHelper.ThrowNotSupportedException("Decoding jpeg files with arithmetic coding is not supported.");
+ if (metadataOnly)
+ {
+ stream.Skip(markerContentByteSize);
+ }
+ else
+ {
+ this.ProcessArithmeticTable(stream, markerContentByteSize);
+ }
+
break;
}
}
// Read on.
- fileMarker = FindNextFileMarker(this.markerBuffer, stream);
+ fileMarker = FindNextFileMarker(stream);
}
}
@@ -449,11 +517,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
///
- /// Returns the correct colorspace based on the image component count and the jpeg frame component id's.
+ /// Returns encoded colorspace based on the adobe APP14 marker.
///
- /// The number of components.
+ /// Number of components.
+ /// Parsed adobe APP14 marker.
/// The
- private JpegColorSpace DeduceJpegColorSpace(byte componentCount)
+ internal static JpegColorSpace DeduceJpegColorSpace(byte componentCount, ref AdobeMarker adobeMarker)
{
if (componentCount == 1)
{
@@ -462,27 +531,48 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (componentCount == 3)
{
- if (!this.adobe.Equals(default) && this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
+ if (adobeMarker.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
{
return JpegColorSpace.RGB;
}
- // If the component Id's are R, G, B in ASCII the colorspace is RGB and not YCbCr.
- if (this.Components[2].Id == 66 && this.Components[1].Id == 71 && this.Components[0].Id == 82)
+ return JpegColorSpace.YCbCr;
+ }
+
+ if (componentCount == 4)
+ {
+ if (adobeMarker.ColorTransform == JpegConstants.Adobe.ColorTransformYcck)
{
- return JpegColorSpace.RGB;
+ return JpegColorSpace.Ycck;
}
- // Some images are poorly encoded and contain incorrect colorspace transform metadata.
- // We ignore that and always fall back to the default colorspace.
+ return JpegColorSpace.Cmyk;
+ }
+
+ JpegThrowHelper.ThrowNotSupportedComponentCount(componentCount);
+ return default;
+ }
+
+ ///
+ /// Returns encoded colorspace based on the component count.
+ ///
+ /// Number of components.
+ /// The
+ internal static JpegColorSpace DeduceJpegColorSpace(byte componentCount)
+ {
+ if (componentCount == 1)
+ {
+ return JpegColorSpace.Grayscale;
+ }
+
+ if (componentCount == 3)
+ {
return JpegColorSpace.YCbCr;
}
if (componentCount == 4)
{
- return this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYcck
- ? JpegColorSpace.Ycck
- : JpegColorSpace.Cmyk;
+ return JpegColorSpace.Cmyk;
}
JpegThrowHelper.ThrowNotSupportedComponentCount(componentCount);
@@ -677,7 +767,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
///
- /// Processes the App1 marker retrieving any stored metadata
+ /// Processes the App1 marker retrieving any stored metadata.
///
/// The input stream.
/// The remaining bytes in the segment block.
@@ -687,7 +777,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
const int XmpMarkerLength = 29;
if (remaining < ExifMarkerLength || this.IgnoreMetadata)
{
- // Skip the application header length
+ // Skip the application header length.
stream.Skip(remaining);
return;
}
@@ -697,12 +787,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException("Bad App1 Marker length.");
}
- // XMP marker is the longest, so read at least that many bytes into temp.
+ // XMP marker is the longer then the EXIF marker, so first try read the EXIF marker bytes.
stream.Read(this.temp, 0, ExifMarkerLength);
+ remaining -= ExifMarkerLength;
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.ExifMarker))
{
- remaining -= ExifMarkerLength;
this.hasExif = true;
byte[] profile = new byte[remaining];
stream.Read(profile, 0, remaining);
@@ -713,7 +803,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
else
{
- // If the EXIF information exceeds 64K, it will be split over multiple APP1 markers
+ // If the EXIF information exceeds 64K, it will be split over multiple APP1 markers.
this.ExtendProfile(ref this.exifData, profile);
}
@@ -722,9 +812,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker.Slice(0, ExifMarkerLength)))
{
- stream.Read(this.temp, 0, XmpMarkerLength - ExifMarkerLength);
- remaining -= XmpMarkerLength;
- if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker.Slice(ExifMarkerLength)))
+ const int remainingXmpMarkerBytes = XmpMarkerLength - ExifMarkerLength;
+ if (remaining < remainingXmpMarkerBytes || this.IgnoreMetadata)
+ {
+ // Skip the application header length.
+ stream.Skip(remaining);
+ return;
+ }
+
+ stream.Read(this.temp, ExifMarkerLength, remainingXmpMarkerBytes);
+ remaining -= remainingXmpMarkerBytes;
+ if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker))
{
this.hasXmp = true;
byte[] profile = new byte[remaining];
@@ -736,7 +834,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
else
{
- // If the XMP information exceeds 64K, it will be split over multiple APP1 markers
+ // If the XMP information exceeds 64K, it will be split over multiple APP1 markers.
this.ExtendProfile(ref this.xmpData, profile);
}
@@ -855,6 +953,47 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
}
+ ///
+ /// Processes a DAC marker, decoding the arithmetic tables.
+ ///
+ /// The input stream.
+ /// The remaining bytes in the segment block.
+ private void ProcessArithmeticTable(BufferedReadStream stream, int remaining)
+ {
+ this.arithmeticDecodingTables ??= new List(4);
+
+ while (remaining > 0)
+ {
+ int tableClassAndIdentifier = stream.ReadByte();
+ remaining--;
+ byte tableClass = (byte)(tableClassAndIdentifier >> 4);
+ byte identifier = (byte)(tableClassAndIdentifier & 0xF);
+
+ byte conditioningTableValue = (byte)stream.ReadByte();
+ remaining--;
+
+ var arithmeticTable = new ArithmeticDecodingTable(tableClass, identifier);
+ arithmeticTable.Configure(conditioningTableValue);
+
+ bool tableEntryReplaced = false;
+ for (int i = 0; i < this.arithmeticDecodingTables.Count; i++)
+ {
+ ArithmeticDecodingTable item = this.arithmeticDecodingTables[i];
+ if (item.TableClass == arithmeticTable.TableClass && item.Identifier == arithmeticTable.Identifier)
+ {
+ this.arithmeticDecodingTables[i] = arithmeticTable;
+ tableEntryReplaced = true;
+ break;
+ }
+ }
+
+ if (!tableEntryReplaced)
+ {
+ this.arithmeticDecodingTables.Add(arithmeticTable);
+ }
+ }
+ }
+
///
/// Reads the adobe image resource block name: a Pascal string (padded to make size even).
///
@@ -902,7 +1041,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
stream.Read(this.temp, 0, MarkerLength);
remaining -= MarkerLength;
- AdobeMarker.TryParse(this.temp, out this.adobe);
+ if (AdobeMarker.TryParse(this.temp, out this.adobe))
+ {
+ this.hasAdobeMarker = true;
+ }
if (remaining > 0)
{
@@ -916,7 +1058,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The input stream.
/// The remaining bytes in the segment block.
///
- /// Thrown if the tables do not match the header
+ /// Thrown if the tables do not match the header.
///
private void ProcessDefineQuantizationTablesMarker(BufferedReadStream stream, int remaining)
{
@@ -1010,9 +1152,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
break;
}
}
-
- // Adjusting table for IDCT step during decompression
- FastFloatingPointDCT.AdjustToIDCT(ref table);
}
}
@@ -1022,8 +1161,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The input stream.
/// The remaining bytes in the segment block.
/// The current frame marker.
- /// Whether to parse metadata only
- private void ProcessStartOfFrameMarker(BufferedReadStream stream, int remaining, in JpegFileMarker frameMarker, bool metadataOnly)
+ /// The jpeg decoding component type.
+ /// Whether to parse metadata only.
+ private void ProcessStartOfFrameMarker(BufferedReadStream stream, int remaining, in JpegFileMarker frameMarker, ComponentType decodingComponentType, bool metadataOnly)
{
if (this.Frame != null)
{
@@ -1035,17 +1175,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException("Multiple SOF markers. Only single frame jpegs supported.");
}
- // Read initial marker definitions
+ // Read initial marker definitions.
const int length = 6;
- stream.Read(this.temp, 0, length);
+ int bytesRead = stream.Read(this.temp, 0, length);
+ if (bytesRead != length)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("SOF marker does not contain enough data.");
+ }
- // 1 byte: Bits/sample precision
+ // 1 byte: Bits/sample precision.
byte precision = this.temp[0];
- // Validate: only 8-bit and 12-bit precisions are supported
+ // Validate: only 8-bit and 12-bit precisions are supported.
if (Array.IndexOf(this.supportedPrecisions, precision) == -1)
{
- JpegThrowHelper.ThrowInvalidImageContentException("Only 8-Bit and 12-Bit precision supported.");
+ JpegThrowHelper.ThrowInvalidImageContentException("Only 8-Bit and 12-Bit precision is supported.");
}
// 2 byte: Height
@@ -1054,18 +1198,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// 2 byte: Width
int frameWidth = (this.temp[3] << 8) | this.temp[4];
- // Validate: width/height > 0 (they are upper-bounded by 2 byte max value so no need to check that)
+ // Validate: width/height > 0 (they are upper-bounded by 2 byte max value so no need to check that).
if (frameHeight == 0 || frameWidth == 0)
{
JpegThrowHelper.ThrowInvalidImageDimensions(frameWidth, frameHeight);
}
- // 1 byte: Number of components
+ // 1 byte: Number of components.
byte componentCount = this.temp[5];
// Validate: componentCount more than 4 can lead to a buffer overflow during stream
- // reading so we must limit it to 4
- // We do not support jpeg images with more than 4 components anyway
+ // reading so we must limit it to 4.
+ // We do not support jpeg images with more than 4 components anyway.
if (componentCount > 4)
{
JpegThrowHelper.ThrowNotSupportedComponentCount(componentCount);
@@ -1093,7 +1237,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int maxH = 0;
int maxV = 0;
int index = 0;
- for (int i = 0; i < componentCount; i++)
+ for (int i = 0; i < this.Frame.Components.Length; i++)
{
// 1 byte: component identifier
byte componentId = this.temp[index];
@@ -1134,15 +1278,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowBadQuantizationTableIndex(quantTableIndex);
}
- var component = new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, componentId, h, v, quantTableIndex, i);
+ IJpegComponent component = decodingComponentType is ComponentType.Huffman ?
+ new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, componentId, h, v, quantTableIndex, i) :
+ new ArithmeticDecodingComponent(this.Configuration.MemoryAllocator, this.Frame, componentId, h, v, quantTableIndex, i);
- this.Frame.Components[i] = component;
+ this.Frame.Components[i] = (JpegComponent)component;
this.Frame.ComponentIds[i] = componentId;
index += componentBytes;
}
- this.ColorSpace = this.DeduceJpegColorSpace(componentCount);
+ this.ColorSpace = this.hasAdobeMarker
+ ? DeduceJpegColorSpace(componentCount, ref this.adobe)
+ : DeduceJpegColorSpace(componentCount);
this.Metadata.GetJpegMetadata().ColorType = this.DeduceJpegColorType();
if (!metadataOnly)
@@ -1164,11 +1312,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
const int codeValuesMaxByteSize = 256;
const int totalBufferSize = codeLengthsByteSize + codeValuesMaxByteSize + HuffmanTable.WorkspaceByteSize;
+ var huffmanScanDecoder = this.scanDecoder as HuffmanScanDecoder;
+ if (huffmanScanDecoder is null)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("missing huffman table data");
+ }
+
int length = remaining;
using (IMemoryOwner buffer = this.Configuration.MemoryAllocator.Allocate(totalBufferSize))
{
Span bufferSpan = buffer.GetSpan();
- Span huffmanLegthsSpan = bufferSpan.Slice(0, codeLengthsByteSize);
+ Span huffmanLengthsSpan = bufferSpan.Slice(0, codeLengthsByteSize);
Span huffmanValuesSpan = bufferSpan.Slice(codeLengthsByteSize, codeValuesMaxByteSize);
Span tableWorkspace = MemoryMarshal.Cast(bufferSpan.Slice(codeLengthsByteSize + codeValuesMaxByteSize));
@@ -1190,12 +1344,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table index: {tableIndex}.");
}
- stream.Read(huffmanLegthsSpan, 1, 16);
+ stream.Read(huffmanLengthsSpan, 1, 16);
int codeLengthSum = 0;
for (int j = 1; j < 17; j++)
{
- codeLengthSum += huffmanLegthsSpan[j];
+ codeLengthSum += huffmanLengthsSpan[j];
}
length -= 17;
@@ -1209,10 +1363,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
i += 17 + codeLengthSum;
- this.scanDecoder.BuildHuffmanTable(
+ huffmanScanDecoder!.BuildHuffmanTable(
tableType,
tableIndex,
- huffmanLegthsSpan,
+ huffmanLengthsSpan,
huffmanValuesSpan.Slice(0, codeLengthSum),
tableWorkspace);
}
@@ -1220,8 +1374,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
///
- /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in
- /// macroblocks
+ /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers,
+ /// in macroblocks.
///
/// The input stream.
/// The remaining bytes in the segment block.
@@ -1232,7 +1386,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.DRI), remaining);
}
- this.scanDecoder.ResetInterval = this.ReadUint16(stream);
+ // Save the reset interval, because it can come before or after the SOF marker.
+ // If the reset interval comes after the SOF marker, the scanDecoder has not been created.
+ this.resetInterval = this.ReadUint16(stream);
+
+ if (this.scanDecoder != null)
+ {
+ this.scanDecoder.ResetInterval = this.resetInterval.Value;
+ }
}
///
@@ -1245,7 +1406,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException("No readable SOFn (Start Of Frame) marker found.");
}
- // 1 byte: Number of components in scan
+ // 1 byte: Number of components in scan.
int selectorsCount = stream.ReadByte();
// Validate: 0 < count <= totalComponents
@@ -1255,11 +1416,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException($"Invalid number of components in scan: {selectorsCount}.");
}
- // Validate: marker must contain exactly (4 + selectorsCount*2) bytes
+ // Validate: Marker must contain exactly (4 + selectorsCount*2) bytes
int selectorsBytes = selectorsCount * 2;
if (remaining != 4 + selectorsBytes)
{
- JpegThrowHelper.ThrowBadMarker("SOS", remaining);
+ JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.SOS), remaining);
}
// selectorsCount*2 bytes: component index + huffman tables indices
@@ -1282,7 +1443,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
}
- // Validate: must be found among registered components
+ // Validate: Must be found among registered components.
if (componentIndex == -1)
{
// TODO: extract as separate method?
@@ -1291,7 +1452,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.Frame.ComponentOrder[i / 2] = (byte)componentIndex;
- JpegComponent component = this.Frame.Components[componentIndex];
+ IJpegComponent component = this.Frame.Components[componentIndex];
// 1 byte: Huffman table selectors.
// 4 bits - dc
@@ -1307,12 +1468,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException($"Invalid huffman table for component:{componentSelectorId}: dc={dcTableIndex}, ac={acTableIndex}");
}
- component.DCHuffmanTableId = dcTableIndex;
- component.ACHuffmanTableId = acTableIndex;
+ component.DcTableId = dcTableIndex;
+ component.AcTableId = acTableIndex;
}
- // 3 bytes: Progressive scan decoding data
- stream.Read(this.temp, 0, 3);
+ // 3 bytes: Progressive scan decoding data.
+ int bytesRead = stream.Read(this.temp, 0, 3);
+ if (bytesRead != 3)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read progressive scan decoding data");
+ }
int spectralStart = this.temp[0];
this.scanDecoder.SpectralStart = spectralStart;
@@ -1324,18 +1489,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.scanDecoder.SuccessiveHigh = successiveApproximation >> 4;
this.scanDecoder.SuccessiveLow = successiveApproximation & 15;
+ if (this.scanDecoder is ArithmeticScanDecoder arithmeticScanDecoder)
+ {
+ arithmeticScanDecoder.InitDecodingTables(this.arithmeticDecodingTables);
+ }
+
this.scanDecoder.ParseEntropyCodedData(selectorsCount);
}
///
- /// Reads a from the stream advancing it by two bytes
+ /// Reads a from the stream advancing it by two bytes.
///
/// The input stream.
/// The
[MethodImpl(InliningOptions.ShortMethod)]
private ushort ReadUint16(BufferedReadStream stream)
{
- stream.Read(this.markerBuffer, 0, 2);
+ int bytesRead = stream.Read(this.markerBuffer, 0, 2);
+ if (bytesRead != 2)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("jpeg stream does not contain enough data, could not read ushort.");
+ }
+
return BinaryPrimitives.ReadUInt16BigEndian(this.markerBuffer);
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
index fc6e3189f..e4e68512a 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index a3cff8f31..c29b34d67 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
diff --git a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs
index 241aa3d8f..dd9884901 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
diff --git a/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs b/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs
index 27f859c47..fbbe210ff 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs
index 0a4b970f4..235d8b487 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
diff --git a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
index b238e45ef..cbaafbc4e 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
@@ -25,6 +25,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowBadMarker(string marker, int length) => throw new InvalidImageContentException($"Marker {marker} has bad length {length}.");
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowNotEnoughBytesForMarker(byte marker) => throw new InvalidImageContentException($"Input stream does not have enough bytes to parse declared contents of the {marker:X2} marker.");
+
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowBadQuantizationTableIndex(int index) => throw new InvalidImageContentException($"Bad Quantization Table index {index}.");
@@ -48,5 +51,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNotSupportedComponentCount(int componentCount) => throw new NotSupportedException($"Images with {componentCount} components are not supported.");
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowNotSupportedColorSpace() => throw new NotSupportedException("Image color space could not be deduced.");
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs
index d154f3490..cc89685a3 100644
--- a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Metadata;
diff --git a/src/ImageSharp/Formats/Pbm/BinaryDecoder.cs b/src/ImageSharp/Formats/Pbm/BinaryDecoder.cs
index 33af30434..c5d28002a 100644
--- a/src/ImageSharp/Formats/Pbm/BinaryDecoder.cs
+++ b/src/ImageSharp/Formats/Pbm/BinaryDecoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
diff --git a/src/ImageSharp/Formats/Pbm/BinaryEncoder.cs b/src/ImageSharp/Formats/Pbm/BinaryEncoder.cs
index 332ab9b50..67cbdd522 100644
--- a/src/ImageSharp/Formats/Pbm/BinaryEncoder.cs
+++ b/src/ImageSharp/Formats/Pbm/BinaryEncoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
diff --git a/src/ImageSharp/Formats/Pbm/BufferedReadStreamExtensions.cs b/src/ImageSharp/Formats/Pbm/BufferedReadStreamExtensions.cs
index 95095edc6..f8556c796 100644
--- a/src/ImageSharp/Formats/Pbm/BufferedReadStreamExtensions.cs
+++ b/src/ImageSharp/Formats/Pbm/BufferedReadStreamExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
diff --git a/src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs b/src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs
index 988d9e560..fb413c371 100644
--- a/src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Pbm
{
diff --git a/src/ImageSharp/Formats/Pbm/MetadataExtensions.cs b/src/ImageSharp/Formats/Pbm/MetadataExtensions.cs
index cce8fb318..d63d66b84 100644
--- a/src/ImageSharp/Formats/Pbm/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Pbm/MetadataExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Metadata;
diff --git a/src/ImageSharp/Formats/Pbm/PbmColorType.cs b/src/ImageSharp/Formats/Pbm/PbmColorType.cs
index 827f19344..955654f01 100644
--- a/src/ImageSharp/Formats/Pbm/PbmColorType.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmColorType.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Pbm
{
diff --git a/src/ImageSharp/Formats/Pbm/PbmComponentType.cs b/src/ImageSharp/Formats/Pbm/PbmComponentType.cs
index 26272021c..de5c0ec94 100644
--- a/src/ImageSharp/Formats/Pbm/PbmComponentType.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmComponentType.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Pbm
{
diff --git a/src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs b/src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs
index 172bda667..fa521a366 100644
--- a/src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmConfigurationModule.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Pbm
{
diff --git a/src/ImageSharp/Formats/Pbm/PbmConstants.cs b/src/ImageSharp/Formats/Pbm/PbmConstants.cs
index 912ffaf85..6c805d406 100644
--- a/src/ImageSharp/Formats/Pbm/PbmConstants.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmConstants.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs
index 2eebbb1d9..835582bb6 100644
--- a/src/ImageSharp/Formats/Pbm/PbmDecoder.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmDecoder.cs
@@ -1,9 +1,8 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Pbm
@@ -30,50 +29,26 @@ namespace SixLabors.ImageSharp.Formats.Pbm
public sealed class PbmDecoder : IImageDecoder, IImageInfoDetector
{
///
- public Image Decode(Configuration configuration, Stream stream)
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(stream, nameof(stream));
var decoder = new PbmDecoderCore(configuration);
- return decoder.Decode(configuration, stream);
+ return decoder.Decode(configuration, stream, cancellationToken);
}
///
- public Image Decode(Configuration configuration, Stream stream)
- => this.Decode(configuration, stream);
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
+ => this.Decode(configuration, stream, cancellationToken);
///
- public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- where TPixel : unmanaged, IPixel
- {
- Guard.NotNull(stream, nameof(stream));
-
- var decoder = new PbmDecoderCore(configuration);
- return decoder.DecodeAsync(configuration, stream, cancellationToken);
- }
-
- ///
- public async Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- => await this.DecodeAsync(configuration, stream, cancellationToken)
- .ConfigureAwait(false);
-
- ///
- public IImageInfo Identify(Configuration configuration, Stream stream)
- {
- Guard.NotNull(stream, nameof(stream));
-
- var decoder = new PbmDecoderCore(configuration);
- return decoder.Identify(configuration, stream);
- }
-
- ///
- public Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
+ public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(stream, nameof(stream));
var decoder = new PbmDecoderCore(configuration);
- return decoder.IdentifyAsync(configuration, stream, cancellationToken);
+ return decoder.Identify(configuration, stream, cancellationToken);
}
}
}
diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs
index 749fc0292..5a549f755 100644
--- a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Threading;
diff --git a/src/ImageSharp/Formats/Pbm/PbmEncoder.cs b/src/ImageSharp/Formats/Pbm/PbmEncoder.cs
index 75d666063..9bfd7192d 100644
--- a/src/ImageSharp/Formats/Pbm/PbmEncoder.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmEncoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
diff --git a/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs
index 9d1f39edf..25c7ee450 100644
--- a/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Text;
diff --git a/src/ImageSharp/Formats/Pbm/PbmEncoding.cs b/src/ImageSharp/Formats/Pbm/PbmEncoding.cs
index be7fb909f..aa42e85c2 100644
--- a/src/ImageSharp/Formats/Pbm/PbmEncoding.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmEncoding.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Pbm
{
diff --git a/src/ImageSharp/Formats/Pbm/PbmFormat.cs b/src/ImageSharp/Formats/Pbm/PbmFormat.cs
index 5ffb49652..e644250a1 100644
--- a/src/ImageSharp/Formats/Pbm/PbmFormat.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmFormat.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
diff --git a/src/ImageSharp/Formats/Pbm/PbmImageFormatDetector.cs b/src/ImageSharp/Formats/Pbm/PbmImageFormatDetector.cs
index 15bacc4de..b8d776a37 100644
--- a/src/ImageSharp/Formats/Pbm/PbmImageFormatDetector.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmImageFormatDetector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs
index a00ae46de..6806ffdb9 100644
--- a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Pbm
{
diff --git a/src/ImageSharp/Formats/Pbm/PlainDecoder.cs b/src/ImageSharp/Formats/Pbm/PlainDecoder.cs
index aeb527dd2..203cd18f1 100644
--- a/src/ImageSharp/Formats/Pbm/PlainDecoder.cs
+++ b/src/ImageSharp/Formats/Pbm/PlainDecoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
diff --git a/src/ImageSharp/Formats/Pbm/PlainEncoder.cs b/src/ImageSharp/Formats/Pbm/PlainEncoder.cs
index a64ae38a7..b858d1e63 100644
--- a/src/ImageSharp/Formats/Pbm/PlainEncoder.cs
+++ b/src/ImageSharp/Formats/Pbm/PlainEncoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs
index afa3b19a2..0d15eebc7 100644
--- a/src/ImageSharp/Formats/PixelTypeInfo.cs
+++ b/src/ImageSharp/Formats/PixelTypeInfo.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
diff --git a/src/ImageSharp/Formats/Png/Adam7.cs b/src/ImageSharp/Formats/Png/Adam7.cs
index 50792cae1..788ff30b6 100644
--- a/src/ImageSharp/Formats/Png/Adam7.cs
+++ b/src/ImageSharp/Formats/Png/Adam7.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
index daf653532..8ad2c5a4a 100644
--- a/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
+++ b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
index 83c638934..b381442bd 100644
--- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
@@ -20,9 +20,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
internal static class AverageFilter
{
///
- /// Decodes the scanline
+ /// Decodes a scanline, which was filtered with the average filter.
///
- /// The scanline to decode
+ /// The scanline to decode.
/// The previous scanline.
/// The bytes per pixel.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -30,11 +30,66 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
{
DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
+ // The Avg filter predicts each pixel as the (truncated) average of a and b:
+ // Average(x) + floor((Raw(x-bpp)+Prior(x))/2)
+ // With pixels positioned like this:
+ // prev: c b
+ // row: a d
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Sse2.IsSupported && bytesPerPixel is 4)
+ {
+ DecodeSse2(scanline, previousScanline);
+ }
+ else
+#endif
+ {
+ DecodeScalar(scanline, previousScanline, bytesPerPixel);
+ }
+ }
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void DecodeSse2(Span scanline, Span previousScanline)
+ {
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
- // Average(x) + floor((Raw(x-bpp)+Prior(x))/2)
- int x = 1;
+ Vector128 d = Vector128.Zero;
+ var ones = Vector128.Create((byte)1);
+
+ int rb = scanline.Length;
+ nint offset = 1;
+ while (rb >= 4)
+ {
+ ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
+ Vector128 a = d;
+ Vector128 b = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref Unsafe.Add(ref prevBaseRef, offset))).AsByte();
+ d = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref scanRef)).AsByte();
+
+ // PNG requires a truncating average, so we can't just use _mm_avg_epu8,
+ // but we can fix it up by subtracting off 1 if it rounded up.
+ Vector128 avg = Sse2.Average(a, b);
+ Vector128 xor = Sse2.Xor(a, b);
+ Vector128 and = Sse2.And(xor, ones);
+ avg = Sse2.Subtract(avg, and);
+ d = Sse2.Add(d, avg);
+
+ // Store the result.
+ Unsafe.As(ref scanRef) = Sse2.ConvertToInt32(d.AsInt32());
+
+ rb -= 4;
+ offset += 4;
+ }
+ }
+#endif
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void DecodeScalar(Span scanline, Span previousScanline, int bytesPerPixel)
+ {
+ ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
+ ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
+
+ nint x = 1;
for (; x <= bytesPerPixel /* Note the <= because x starts at 1 */; ++x)
{
ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);
@@ -52,13 +107,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
}
///
- /// Encodes the scanline
+ /// Encodes a scanline with the average filter applied.
///
- /// The scanline to encode
+ /// The scanline to encode.
/// The previous scanline.
/// The filtered scanline result.
/// The bytes per pixel.
- /// The sum of the total variance of the filtered row
+ /// The sum of the total variance of the filtered row.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, int bytesPerPixel, out int sum)
{
diff --git a/src/ImageSharp/Formats/Png/Filters/FilterType.cs b/src/ImageSharp/Formats/Png/Filters/FilterType.cs
index bd6aa3c59..a3a5b51b6 100644
--- a/src/ImageSharp/Formats/Png/Filters/FilterType.cs
+++ b/src/ImageSharp/Formats/Png/Filters/FilterType.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png.Filters
{
diff --git a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
index 8fa4b3aad..fd2d9eab4 100644
--- a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
index 6a89a1122..03d883454 100644
--- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
@@ -22,9 +22,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
internal static class PaethFilter
{
///
- /// Decodes the scanline
+ /// Decodes a scanline, which was filtered with the paeth filter.
///
- /// The scanline to decode
+ /// The scanline to decode.
/// The previous scanline.
/// The bytes per pixel.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -32,6 +32,86 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
{
DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
+ // Paeth tries to predict pixel d using the pixel to the left of it, a,
+ // and two pixels from the previous row, b and c:
+ // prev: c b
+ // row: a d
+ // The Paeth function predicts d to be whichever of a, b, or c is nearest to
+ // p = a + b - c.
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Sse41.IsSupported && bytesPerPixel is 4)
+ {
+ DecodeSse41(scanline, previousScanline);
+ }
+ else
+#endif
+ {
+ DecodeScalar(scanline, previousScanline, bytesPerPixel);
+ }
+ }
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void DecodeSse41(Span scanline, Span previousScanline)
+ {
+ ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
+ ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
+
+ Vector128 b = Vector128.Zero;
+ Vector128 d = Vector128.Zero;
+
+ int rb = scanline.Length;
+ nint offset = 1;
+ while (rb >= 4)
+ {
+ ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
+
+ // It's easiest to do this math (particularly, deal with pc) with 16-bit intermediates.
+ Vector128 c = b;
+ Vector128 a = d;
+ b = Sse2.UnpackLow(
+ Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref Unsafe.Add(ref prevBaseRef, offset))).AsByte(),
+ Vector128.Zero);
+ d = Sse2.UnpackLow(
+ Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref scanRef)).AsByte(),
+ Vector128.Zero);
+
+ // (p-a) == (a+b-c - a) == (b-c)
+ Vector128 pa = Sse2.Subtract(b.AsInt16(), c.AsInt16());
+
+ // (p-b) == (a+b-c - b) == (a-c)
+ Vector128 pb = Sse2.Subtract(a.AsInt16(), c.AsInt16());
+
+ // (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c)
+ Vector128 pc = Sse2.Add(pa.AsInt16(), pb.AsInt16());
+
+ pa = Ssse3.Abs(pa.AsInt16()).AsInt16(); /* |p-a| */
+ pb = Ssse3.Abs(pb.AsInt16()).AsInt16(); /* |p-b| */
+ pc = Ssse3.Abs(pc.AsInt16()).AsInt16(); /* |p-c| */
+
+ Vector128 smallest = Sse2.Min(pc, Sse2.Min(pa, pb));
+
+ // Paeth breaks ties favoring a over b over c.
+ Vector128 mask = Sse41.BlendVariable(c, b, Sse2.CompareEqual(smallest, pb).AsByte());
+ Vector128 nearest = Sse41.BlendVariable(mask, a, Sse2.CompareEqual(smallest, pa).AsByte());
+
+ // Note `_epi8`: we need addition to wrap modulo 255.
+ d = Sse2.Add(d, nearest);
+
+ // Store the result.
+ Unsafe.As(ref scanRef) = Sse2.ConvertToInt32(Sse2.PackUnsignedSaturate(d.AsInt16(), d.AsInt16()).AsInt32());
+
+ rb -= 4;
+ offset += 4;
+ }
+ }
+
+#endif
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void DecodeScalar(Span scanline, Span previousScanline, int bytesPerPixel)
+ {
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
@@ -56,13 +136,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
}
///
- /// Encodes the scanline
+ /// Encodes a scanline and applies the paeth filter.
///
/// The scanline to encode
/// The previous scanline.
/// The filtered scanline result.
/// The bytes per pixel.
- /// The sum of the total variance of the filtered row
+ /// The sum of the total variance of the filtered row.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, int bytesPerPixel, out int sum)
{
diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
index c28b877e4..a7044837e 100644
--- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
@@ -21,17 +21,57 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
internal static class SubFilter
{
///
- /// Decodes the scanline
+ /// Decodes a scanline, which was filtered with the sub filter.
///
- /// The scanline to decode
+ /// The scanline to decode.
/// The bytes per pixel.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decode(Span scanline, int bytesPerPixel)
+ {
+ // The Sub filter predicts each pixel as the previous pixel.
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Sse2.IsSupported && bytesPerPixel is 4)
+ {
+ DecodeSse2(scanline);
+ }
+ else
+#endif
+ {
+ DecodeScalar(scanline, bytesPerPixel);
+ }
+ }
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+ private static void DecodeSse2(Span scanline)
+ {
+ ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
+
+ Vector128 d = Vector128.Zero;
+
+ int rb = scanline.Length;
+ nint offset = 1;
+ while (rb >= 4)
+ {
+ ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
+ Vector128 a = d;
+ d = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref scanRef)).AsByte();
+
+ d = Sse2.Add(d, a);
+
+ Unsafe.As(ref scanRef) = Sse2.ConvertToInt32(d.AsInt32());
+
+ rb -= 4;
+ offset += 4;
+ }
+ }
+#endif
+
+ private static void DecodeScalar(Span scanline, int bytesPerPixel)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
// Sub(x) + Raw(x-bpp)
- int x = bytesPerPixel + 1;
+ nint x = bytesPerPixel + 1;
Unsafe.Add(ref scanBaseRef, x);
for (; x < scanline.Length; ++x)
{
@@ -42,12 +82,12 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
}
///
- /// Encodes the scanline
+ /// Encodes a scanline with the sup filter applied.
///
- /// The scanline to encode
+ /// The scanline to encode.
/// The filtered scanline result.
/// The bytes per pixel.
- /// The sum of the total variance of the filtered row
+ /// The sum of the total variance of the filtered row.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(ReadOnlySpan scanline, ReadOnlySpan result, int bytesPerPixel, out int sum)
{
diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
index 7e0286991..c5003d111 100644
--- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
@@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
internal static class UpFilter
{
///
- /// Decodes the scanline
+ /// Decodes a scanline, which was filtered with the up filter.
///
/// The scanline to decode
/// The previous scanline.
@@ -30,6 +30,91 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
{
DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Avx2.IsSupported)
+ {
+ DecodeAvx2(scanline, previousScanline);
+ }
+ else if (Sse2.IsSupported)
+ {
+ DecodeSse2(scanline, previousScanline);
+ }
+ else
+#endif
+ {
+ DecodeScalar(scanline, previousScanline);
+ }
+ }
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+ private static void DecodeAvx2(Span scanline, Span previousScanline)
+ {
+ ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
+ ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
+
+ // Up(x) + Prior(x)
+ int rb = scanline.Length;
+ nint offset = 1;
+ const int bytesPerBatch = 32;
+ while (rb >= bytesPerBatch)
+ {
+ ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
+ Vector256 current = Unsafe.As>(ref scanRef);
+ Vector256 up = Unsafe.As>(ref Unsafe.Add(ref prevBaseRef, offset));
+
+ Vector256 sum = Avx2.Add(up, current);
+ Unsafe.As>(ref scanRef) = sum;
+
+ offset += bytesPerBatch;
+ rb -= bytesPerBatch;
+ }
+
+ // Handle left over.
+ for (nint i = offset; i < scanline.Length; i++)
+ {
+ ref byte scan = ref Unsafe.Add(ref scanBaseRef, offset);
+ byte above = Unsafe.Add(ref prevBaseRef, offset);
+ scan = (byte)(scan + above);
+ offset++;
+ }
+ }
+
+ private static void DecodeSse2(Span scanline, Span previousScanline)
+ {
+ ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
+ ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
+
+ // Up(x) + Prior(x)
+ int rb = scanline.Length;
+ nint offset = 1;
+ const int bytesPerBatch = 16;
+ while (rb >= bytesPerBatch)
+ {
+ ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
+ Vector128 current = Unsafe.As>(ref scanRef);
+ Vector128 up = Unsafe.As>(ref Unsafe.Add(ref prevBaseRef, offset));
+
+ Vector128 sum = Sse2.Add(up, current);
+ Unsafe.As>(ref scanRef) = sum;
+
+ offset += bytesPerBatch;
+ rb -= bytesPerBatch;
+ }
+
+ // Handle left over.
+ for (nint i = offset; i < scanline.Length; i++)
+ {
+ ref byte scan = ref Unsafe.Add(ref scanBaseRef, offset);
+ byte above = Unsafe.Add(ref prevBaseRef, offset);
+ scan = (byte)(scan + above);
+ offset++;
+ }
+ }
+#endif
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void DecodeScalar(Span scanline, Span previousScanline)
+ {
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
@@ -43,12 +128,12 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
}
///
- /// Encodes the scanline
+ /// Encodes a scanline with the up filter applied.
///
- /// The scanline to encode
+ /// The scanline to encode.
/// The previous scanline.
/// The filtered scanline result.
- /// The sum of the total variance of the filtered row
+ /// The sum of the total variance of the filtered row.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, out int sum)
{
diff --git a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
index 4b09c5b1c..873abc1fd 100644
--- a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
+++ b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png
{
diff --git a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
index 2c05019ed..1bc785eed 100644
--- a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Quantization;
diff --git a/src/ImageSharp/Formats/Png/MetadataExtensions.cs b/src/ImageSharp/Formats/Png/MetadataExtensions.cs
index b7c2cccf5..24417eb1b 100644
--- a/src/ImageSharp/Formats/Png/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Png/MetadataExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Metadata;
diff --git a/src/ImageSharp/Formats/Png/PngBitDepth.cs b/src/ImageSharp/Formats/Png/PngBitDepth.cs
index e03f80f28..50e76326e 100644
--- a/src/ImageSharp/Formats/Png/PngBitDepth.cs
+++ b/src/ImageSharp/Formats/Png/PngBitDepth.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
// Note the value assignment, This will allow us to add 1, 2, and 4 bit encoding when we support it.
namespace SixLabors.ImageSharp.Formats.Png
diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs
index 7b5f390f1..09270cd52 100644
--- a/src/ImageSharp/Formats/Png/PngChunk.cs
+++ b/src/ImageSharp/Formats/Png/PngChunk.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Buffers;
diff --git a/src/ImageSharp/Formats/Png/PngChunkFilter.cs b/src/ImageSharp/Formats/Png/PngChunkFilter.cs
index 4d6a20bc9..f0feb9e6a 100644
--- a/src/ImageSharp/Formats/Png/PngChunkFilter.cs
+++ b/src/ImageSharp/Formats/Png/PngChunkFilter.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/Png/PngChunkType.cs b/src/ImageSharp/Formats/Png/PngChunkType.cs
index 39f19f232..213a4a793 100644
--- a/src/ImageSharp/Formats/Png/PngChunkType.cs
+++ b/src/ImageSharp/Formats/Png/PngChunkType.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png
{
diff --git a/src/ImageSharp/Formats/Png/PngColorType.cs b/src/ImageSharp/Formats/Png/PngColorType.cs
index ce631b1a1..139554c7c 100644
--- a/src/ImageSharp/Formats/Png/PngColorType.cs
+++ b/src/ImageSharp/Formats/Png/PngColorType.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png
{
diff --git a/src/ImageSharp/Formats/Png/PngCompressionLevel.cs b/src/ImageSharp/Formats/Png/PngCompressionLevel.cs
index 961f9b05b..df098b7be 100644
--- a/src/ImageSharp/Formats/Png/PngCompressionLevel.cs
+++ b/src/ImageSharp/Formats/Png/PngCompressionLevel.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.ComponentModel;
diff --git a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs
index 9a1f4b2b3..bbf2f4cb8 100644
--- a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs
+++ b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png
{
diff --git a/src/ImageSharp/Formats/Png/PngConstants.cs b/src/ImageSharp/Formats/Png/PngConstants.cs
index fcc8fd992..69fae6af9 100644
--- a/src/ImageSharp/Formats/Png/PngConstants.cs
+++ b/src/ImageSharp/Formats/Png/PngConstants.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Collections.Generic;
diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs
index 04e70c51d..f1a03b83e 100644
--- a/src/ImageSharp/Formats/Png/PngDecoder.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoder.cs
@@ -1,9 +1,8 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Png
@@ -17,18 +16,18 @@ namespace SixLabors.ImageSharp.Formats.Png
public bool IgnoreMetadata { get; set; }
///
- public Image Decode(Configuration configuration, Stream stream)
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
PngDecoderCore decoder = new(configuration, this);
- return decoder.Decode(configuration, stream);
+ return decoder.Decode(configuration, stream, cancellationToken);
}
///
- public Image Decode(Configuration configuration, Stream stream)
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
{
PngDecoderCore decoder = new(configuration, true);
- IImageInfo info = decoder.Identify(configuration, stream);
+ IImageInfo info = decoder.Identify(configuration, stream, cancellationToken);
stream.Position = 0;
PngMetadata meta = info.Metadata.GetPngMetadata();
@@ -40,118 +39,49 @@ namespace SixLabors.ImageSharp.Formats.Png
if (bits == PngBitDepth.Bit16)
{
return !meta.HasTransparency
- ? this.Decode(configuration, stream)
- : this.Decode(configuration, stream);
+ ? this.Decode(configuration, stream, cancellationToken)
+ : this.Decode(configuration, stream, cancellationToken);
}
return !meta.HasTransparency
- ? this.Decode(configuration, stream)
- : this.Decode(configuration, stream);
+ ? this.Decode(configuration, stream, cancellationToken)
+ : this.Decode(configuration, stream, cancellationToken);
case PngColorType.Rgb:
if (bits == PngBitDepth.Bit16)
{
return !meta.HasTransparency
- ? this.Decode(configuration, stream)
- : this.Decode(configuration, stream);
+ ? this.Decode(configuration, stream, cancellationToken)
+ : this.Decode(configuration, stream, cancellationToken);
}
return !meta.HasTransparency
- ? this.Decode(configuration, stream)
- : this.Decode(configuration, stream);
+ ? this.Decode(configuration, stream, cancellationToken)
+ : this.Decode(configuration, stream, cancellationToken);
case PngColorType.Palette:
- return this.Decode(configuration, stream);
+ return this.Decode(configuration, stream, cancellationToken);
case PngColorType.GrayscaleWithAlpha:
return (bits == PngBitDepth.Bit16)
- ? this.Decode(configuration, stream)
- : this.Decode(configuration, stream);
+ ? this.Decode(configuration, stream, cancellationToken)
+ : this.Decode(configuration, stream, cancellationToken);
case PngColorType.RgbWithAlpha:
return (bits == PngBitDepth.Bit16)
- ? this.Decode(configuration, stream)
- : this.Decode(configuration, stream);
+ ? this.Decode(configuration, stream, cancellationToken)
+ : this.Decode(configuration, stream, cancellationToken);
default:
- return this.Decode(configuration, stream);
+ return this.Decode(configuration, stream, cancellationToken);
}
}
///
- public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- where TPixel : unmanaged, IPixel
- {
- PngDecoderCore decoder = new(configuration, this);
- return decoder.DecodeAsync(configuration, stream, cancellationToken);
- }
-
- ///
- public async Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- {
- PngDecoderCore decoder = new(configuration, true);
- IImageInfo info = await decoder.IdentifyAsync(configuration, stream, cancellationToken).ConfigureAwait(false);
- stream.Position = 0;
-
- PngMetadata meta = info.Metadata.GetPngMetadata();
- PngColorType color = meta.ColorType.GetValueOrDefault();
- PngBitDepth bits = meta.BitDepth.GetValueOrDefault();
- switch (color)
- {
- case PngColorType.Grayscale:
- if (bits == PngBitDepth.Bit16)
- {
- return !meta.HasTransparency
- ? await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false)
- : await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false);
- }
-
- return !meta.HasTransparency
- ? await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false)
- : await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false);
-
- case PngColorType.Rgb:
- if (bits == PngBitDepth.Bit16)
- {
- return !meta.HasTransparency
- ? await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false)
- : await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false);
- }
-
- return !meta.HasTransparency
- ? await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false)
- : await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false);
-
- case PngColorType.Palette:
- return await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false);
-
- case PngColorType.GrayscaleWithAlpha:
- return (bits == PngBitDepth.Bit16)
- ? await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false)
- : await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false);
-
- case PngColorType.RgbWithAlpha:
- return (bits == PngBitDepth.Bit16)
- ? await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false)
- : await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false);
-
- default:
- return await this.DecodeAsync(configuration, stream, cancellationToken).ConfigureAwait(false);
- }
- }
-
- ///
- public IImageInfo Identify(Configuration configuration, Stream stream)
- {
- PngDecoderCore decoder = new(configuration, this);
- return decoder.Identify(configuration, stream);
- }
-
- ///
- public Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
+ public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken)
{
PngDecoderCore decoder = new(configuration, this);
- return decoder.IdentifyAsync(configuration, stream, cancellationToken);
+ return decoder.Identify(configuration, stream, cancellationToken);
}
}
}
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index f5fc86ee4..784d9aa11 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
@@ -19,6 +19,7 @@ using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
+using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
using SixLabors.ImageSharp.PixelFormats;
@@ -205,6 +206,9 @@ namespace SixLabors.ImageSharp.Formats.Png
this.MergeOrSetExifProfile(metadata, new ExifProfile(exifData), replaceExistingKeys: true);
}
+ break;
+ case PngChunkType.EmbeddedColorProfile:
+ this.ReadColorProfileChunk(metadata, chunk.Data.GetSpan());
break;
case PngChunkType.End:
goto EOF;
@@ -227,10 +231,16 @@ namespace SixLabors.ImageSharp.Formats.Png
return image;
}
+ catch
+ {
+ image?.Dispose();
+ throw;
+ }
finally
{
this.scanline?.Dispose();
this.previousScanline?.Dispose();
+ this.nextChunk?.Data?.Dispose();
}
}
@@ -336,6 +346,14 @@ namespace SixLabors.ImageSharp.Formats.Png
break;
case PngChunkType.End:
goto EOF;
+
+ default:
+ if (this.colorMetadataOnly)
+ {
+ this.SkipChunkDataAndCrc(chunk);
+ }
+
+ break;
}
}
finally
@@ -429,10 +447,17 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The metadata to read to.
/// The data containing physical data.
private void ReadGammaChunk(PngMetadata pngMetadata, ReadOnlySpan data)
+ {
+ if (data.Length < 4)
+ {
+ // Ignore invalid gamma chunks.
+ return;
+ }
- // The value is encoded as a 4-byte unsigned integer, representing gamma times 100000.
// For example, a gamma of 1/2.2 would be stored as 45455.
- => pngMetadata.Gamma = BinaryPrimitives.ReadUInt32BigEndian(data) * 1e-5F;
+ // The value is encoded as a 4-byte unsigned integer, representing gamma times 100000.
+ pngMetadata.Gamma = BinaryPrimitives.ReadUInt32BigEndian(data) * 1e-5F;
+ }
///
/// Initializes the image and various buffers needed for processing
@@ -457,6 +482,8 @@ namespace SixLabors.ImageSharp.Formats.Png
this.bytesPerSample = this.header.BitDepth / 8;
}
+ this.previousScanline?.Dispose();
+ this.scanline?.Dispose();
this.previousScanline = this.memoryAllocator.Allocate(this.bytesPerScanline, AllocationOptions.Clean);
this.scanline = this.Configuration.MemoryAllocator.Allocate(this.bytesPerScanline, AllocationOptions.Clean);
}
@@ -1151,6 +1178,76 @@ namespace SixLabors.ImageSharp.Formats.Png
return true;
}
+ ///
+ /// Reads the color profile chunk. The data is stored similar to the zTXt chunk.
+ ///
+ /// The metadata.
+ /// The bytes containing the profile.
+ private void ReadColorProfileChunk(ImageMetadata metadata, ReadOnlySpan data)
+ {
+ int zeroIndex = data.IndexOf((byte)0);
+ if (zeroIndex is < PngConstants.MinTextKeywordLength or > PngConstants.MaxTextKeywordLength)
+ {
+ return;
+ }
+
+ byte compressionMethod = data[zeroIndex + 1];
+ if (compressionMethod != 0)
+ {
+ // Only compression method 0 is supported (zlib datastream with deflate compression).
+ return;
+ }
+
+ ReadOnlySpan keywordBytes = data.Slice(0, zeroIndex);
+ if (!this.TryReadTextKeyword(keywordBytes, out string name))
+ {
+ return;
+ }
+
+ ReadOnlySpan compressedData = data.Slice(zeroIndex + 2);
+
+ if (this.TryUncompressZlibData(compressedData, out byte[] iccpProfileBytes))
+ {
+ metadata.IccProfile = new IccProfile(iccpProfileBytes);
+ }
+ }
+
+ ///
+ /// Tries to un-compress zlib compressed data.
+ ///
+ /// The compressed data.
+ /// The uncompressed bytes array.
+ /// True, if de-compressing was successful.
+ private unsafe bool TryUncompressZlibData(ReadOnlySpan compressedData, out byte[] uncompressedBytesArray)
+ {
+ fixed (byte* compressedDataBase = compressedData)
+ {
+ using (IMemoryOwner destBuffer = this.memoryAllocator.Allocate(this.Configuration.StreamProcessingBufferSize))
+ using (var memoryStreamOutput = new MemoryStream(compressedData.Length))
+ using (var memoryStreamInput = new UnmanagedMemoryStream(compressedDataBase, compressedData.Length))
+ using (var bufferedStream = new BufferedReadStream(this.Configuration, memoryStreamInput))
+ using (var inflateStream = new ZlibInflateStream(bufferedStream))
+ {
+ Span destUncompressedData = destBuffer.GetSpan();
+ if (!inflateStream.AllocateNewBytes(compressedData.Length, false))
+ {
+ uncompressedBytesArray = Array.Empty();
+ return false;
+ }
+
+ int bytesRead = inflateStream.CompressedStream.Read(destUncompressedData, 0, destUncompressedData.Length);
+ while (bytesRead != 0)
+ {
+ memoryStreamOutput.Write(destUncompressedData.Slice(0, bytesRead));
+ bytesRead = inflateStream.CompressedStream.Read(destUncompressedData, 0, destUncompressedData.Length);
+ }
+
+ uncompressedBytesArray = memoryStreamOutput.ToArray();
+ return true;
+ }
+ }
+ }
+
///
/// Compares two ReadOnlySpan<char>s in a case-insensitive method.
/// This is only needed because older frameworks are missing the extension method.
@@ -1283,7 +1380,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else if (this.IsXmpTextData(keywordBytes))
{
- XmpProfile xmpProfile = new XmpProfile(data.Slice(dataStartIdx).ToArray());
+ var xmpProfile = new XmpProfile(data.Slice(dataStartIdx).ToArray());
metadata.XmpProfile = xmpProfile;
}
else
@@ -1302,29 +1399,14 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The .
private bool TryUncompressTextData(ReadOnlySpan compressedData, Encoding encoding, out string value)
{
- using (var memoryStream = new MemoryStream(compressedData.ToArray()))
- using (var bufferedStream = new BufferedReadStream(this.Configuration, memoryStream))
- using (var inflateStream = new ZlibInflateStream(bufferedStream))
+ if (this.TryUncompressZlibData(compressedData, out byte[] uncompressedData))
{
- if (!inflateStream.AllocateNewBytes(compressedData.Length, false))
- {
- value = null;
- return false;
- }
-
- var uncompressedBytes = new List();
-
- // Note: this uses a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here.
- int bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
- while (bytesRead != 0)
- {
- uncompressedBytes.AddRange(this.buffer.AsSpan(0, bytesRead).ToArray());
- bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
- }
-
- value = encoding.GetString(uncompressedBytes.ToArray());
+ value = encoding.GetString(uncompressedData);
return true;
}
+
+ value = null;
+ return false;
}
///
@@ -1344,6 +1426,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
if (chunk.Type == PngChunkType.Data)
{
+ chunk.Data?.Dispose();
return chunk.Length;
}
@@ -1438,6 +1521,9 @@ namespace SixLabors.ImageSharp.Formats.Png
if (validCrc != inputCrc)
{
string chunkTypeName = Encoding.ASCII.GetString(chunkType);
+
+ // ensure when throwing we dispose the data back to the memory allocator
+ chunk.Data?.Dispose();
PngThrowHelper.ThrowInvalidChunkCrc(chunkTypeName);
}
}
diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs
index e72e8d3d5..31e0c17c5 100644
--- a/src/ImageSharp/Formats/Png/PngEncoder.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoder.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index c443c0fcf..9ae8f299a 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
@@ -87,6 +87,11 @@ namespace SixLabors.ImageSharp.Formats.Png
///
private IMemoryOwner currentScanline;
+ ///
+ /// The color profile name.
+ ///
+ private const string ColorProfileName = "ICC Profile";
+
///
/// Initializes a new instance of the class.
///
@@ -134,6 +139,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.WriteHeaderChunk(stream);
this.WriteGammaChunk(stream);
+ this.WriteColorProfileChunk(stream, metadata);
this.WritePaletteChunk(stream, quantized);
this.WriteTransparencyChunk(stream, pngMetadata);
this.WritePhysicalChunk(stream, metadata);
@@ -656,7 +662,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
///
- /// Writes an iTXT chunk, containing the XMP metdata to the stream, if such profile is present in the metadata.
+ /// Writes an iTXT chunk, containing the XMP metadata to the stream, if such profile is present in the metadata.
///
/// The containing image data.
/// The image metadata.
@@ -673,7 +679,7 @@ namespace SixLabors.ImageSharp.Formats.Png
return;
}
- var xmpData = meta.XmpProfile.Data;
+ byte[] xmpData = meta.XmpProfile.Data;
if (xmpData.Length == 0)
{
@@ -687,19 +693,49 @@ namespace SixLabors.ImageSharp.Formats.Png
PngConstants.XmpKeyword.CopyTo(payload);
int bytesWritten = PngConstants.XmpKeyword.Length;
- // Write the iTxt header (all zeros in this case)
- payload[bytesWritten++] = 0;
- payload[bytesWritten++] = 0;
- payload[bytesWritten++] = 0;
- payload[bytesWritten++] = 0;
- payload[bytesWritten++] = 0;
+ // Write the iTxt header (all zeros in this case).
+ Span iTxtHeader = payload.Slice(bytesWritten);
+ iTxtHeader[4] = 0;
+ iTxtHeader[3] = 0;
+ iTxtHeader[2] = 0;
+ iTxtHeader[1] = 0;
+ iTxtHeader[0] = 0;
+ bytesWritten += 5;
- // And the XMP data itself
+ // And the XMP data itself.
xmpData.CopyTo(payload.Slice(bytesWritten));
this.WriteChunk(stream, PngChunkType.InternationalText, payload);
}
}
+ ///
+ /// Writes the color profile chunk.
+ ///
+ /// The stream to write to.
+ /// The image meta data.
+ private void WriteColorProfileChunk(Stream stream, ImageMetadata metaData)
+ {
+ if (metaData.IccProfile is null)
+ {
+ return;
+ }
+
+ byte[] iccProfileBytes = metaData.IccProfile.ToByteArray();
+
+ byte[] compressedData = this.GetZlibCompressedBytes(iccProfileBytes);
+ int payloadLength = ColorProfileName.Length + compressedData.Length + 2;
+ using (IMemoryOwner owner = this.memoryAllocator.Allocate(payloadLength))
+ {
+ Span outputBytes = owner.GetSpan();
+ PngConstants.Encoding.GetBytes(ColorProfileName).CopyTo(outputBytes);
+ int bytesWritten = ColorProfileName.Length;
+ outputBytes[bytesWritten++] = 0; // Null separator.
+ outputBytes[bytesWritten++] = 0; // Compression.
+ compressedData.CopyTo(outputBytes.Slice(bytesWritten));
+ this.WriteChunk(stream, PngChunkType.EmbeddedColorProfile, outputBytes);
+ }
+ }
+
///
/// Writes a text chunk to the stream. Can be either a tTXt, iTXt or zTXt chunk,
/// depending whether the text contains any latin characters or should be compressed.
@@ -727,13 +763,12 @@ namespace SixLabors.ImageSharp.Formats.Png
}
}
- if (hasUnicodeCharacters || (!string.IsNullOrWhiteSpace(textData.LanguageTag) ||
- !string.IsNullOrWhiteSpace(textData.TranslatedKeyword)))
+ if (hasUnicodeCharacters || (!string.IsNullOrWhiteSpace(textData.LanguageTag) || !string.IsNullOrWhiteSpace(textData.TranslatedKeyword)))
{
// Write iTXt chunk.
byte[] keywordBytes = PngConstants.Encoding.GetBytes(textData.Keyword);
byte[] textBytes = textData.Value.Length > this.options.TextCompressionThreshold
- ? this.GetCompressedTextBytes(PngConstants.TranslatedEncoding.GetBytes(textData.Value))
+ ? this.GetZlibCompressedBytes(PngConstants.TranslatedEncoding.GetBytes(textData.Value))
: PngConstants.TranslatedEncoding.GetBytes(textData.Value);
byte[] translatedKeyword = PngConstants.TranslatedEncoding.GetBytes(textData.TranslatedKeyword);
@@ -772,18 +807,17 @@ namespace SixLabors.ImageSharp.Formats.Png
if (textData.Value.Length > this.options.TextCompressionThreshold)
{
// Write zTXt chunk.
- byte[] compressedData =
- this.GetCompressedTextBytes(PngConstants.Encoding.GetBytes(textData.Value));
+ byte[] compressedData = this.GetZlibCompressedBytes(PngConstants.Encoding.GetBytes(textData.Value));
int payloadLength = textData.Keyword.Length + compressedData.Length + 2;
using (IMemoryOwner owner = this.memoryAllocator.Allocate(payloadLength))
{
Span outputBytes = owner.GetSpan();
PngConstants.Encoding.GetBytes(textData.Keyword).CopyTo(outputBytes);
int bytesWritten = textData.Keyword.Length;
- outputBytes[bytesWritten++] = 0;
- outputBytes[bytesWritten++] = 0;
+ outputBytes[bytesWritten++] = 0; // Null separator.
+ outputBytes[bytesWritten++] = 0; // Compression.
compressedData.CopyTo(outputBytes.Slice(bytesWritten));
- this.WriteChunk(stream, PngChunkType.CompressedText, outputBytes.ToArray());
+ this.WriteChunk(stream, PngChunkType.CompressedText, outputBytes);
}
}
else
@@ -796,9 +830,8 @@ namespace SixLabors.ImageSharp.Formats.Png
PngConstants.Encoding.GetBytes(textData.Keyword).CopyTo(outputBytes);
int bytesWritten = textData.Keyword.Length;
outputBytes[bytesWritten++] = 0;
- PngConstants.Encoding.GetBytes(textData.Value)
- .CopyTo(outputBytes.Slice(bytesWritten));
- this.WriteChunk(stream, PngChunkType.Text, outputBytes.ToArray());
+ PngConstants.Encoding.GetBytes(textData.Value).CopyTo(outputBytes.Slice(bytesWritten));
+ this.WriteChunk(stream, PngChunkType.Text, outputBytes);
}
}
}
@@ -808,15 +841,15 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// Compresses a given text using Zlib compression.
///
- /// The text bytes to compress.
- /// The compressed text byte array.
- private byte[] GetCompressedTextBytes(byte[] textBytes)
+ /// The bytes to compress.
+ /// The compressed byte array.
+ private byte[] GetZlibCompressedBytes(byte[] dataBytes)
{
using (var memoryStream = new MemoryStream())
{
using (var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, this.options.CompressionLevel))
{
- deflateStream.Write(textBytes);
+ deflateStream.Write(dataBytes);
}
return memoryStream.ToArray();
diff --git a/src/ImageSharp/Formats/Png/PngEncoderHelpers.cs b/src/ImageSharp/Formats/Png/PngEncoderHelpers.cs
index 5717ff0d6..99f64f238 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderHelpers.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderHelpers.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs
index 0bcea037a..aaf1071cf 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Quantization;
diff --git a/src/ImageSharp/Formats/Png/PngEncoderOptionsHelpers.cs b/src/ImageSharp/Formats/Png/PngEncoderOptionsHelpers.cs
index 1250db6fe..bed20fd41 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderOptionsHelpers.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderOptionsHelpers.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using SixLabors.ImageSharp.Advanced;
diff --git a/src/ImageSharp/Formats/Png/PngFilterMethod.cs b/src/ImageSharp/Formats/Png/PngFilterMethod.cs
index c6d466ac3..0e76fc91b 100644
--- a/src/ImageSharp/Formats/Png/PngFilterMethod.cs
+++ b/src/ImageSharp/Formats/Png/PngFilterMethod.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png
{
diff --git a/src/ImageSharp/Formats/Png/PngFormat.cs b/src/ImageSharp/Formats/Png/PngFormat.cs
index 3c867e0bd..df222008b 100644
--- a/src/ImageSharp/Formats/Png/PngFormat.cs
+++ b/src/ImageSharp/Formats/Png/PngFormat.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
diff --git a/src/ImageSharp/Formats/Png/PngHeader.cs b/src/ImageSharp/Formats/Png/PngHeader.cs
index d54050e02..da747a189 100644
--- a/src/ImageSharp/Formats/Png/PngHeader.cs
+++ b/src/ImageSharp/Formats/Png/PngHeader.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
diff --git a/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs b/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs
index d2ce98847..d8bcbac0f 100644
--- a/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs
+++ b/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
diff --git a/src/ImageSharp/Formats/Png/PngInterlaceMode.cs b/src/ImageSharp/Formats/Png/PngInterlaceMode.cs
index 9f97c2e4e..4e6c2bbd0 100644
--- a/src/ImageSharp/Formats/Png/PngInterlaceMode.cs
+++ b/src/ImageSharp/Formats/Png/PngInterlaceMode.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png
{
diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs
index 185e7bf4a..52d70906d 100644
--- a/src/ImageSharp/Formats/Png/PngMetadata.cs
+++ b/src/ImageSharp/Formats/Png/PngMetadata.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
using SixLabors.ImageSharp.PixelFormats;
diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
index 58fa5aca8..41b5c370d 100644
--- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
+++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers.Binary;
@@ -240,6 +240,11 @@ namespace SixLabors.ImageSharp.Formats.Png
byte[] paletteAlpha)
where TPixel : unmanaged, IPixel
{
+ if (palette.IsEmpty)
+ {
+ PngThrowHelper.ThrowMissingPalette();
+ }
+
TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
diff --git a/src/ImageSharp/Formats/Png/PngTextData.cs b/src/ImageSharp/Formats/Png/PngTextData.cs
index 6c391d62a..3d495cba6 100644
--- a/src/ImageSharp/Formats/Png/PngTextData.cs
+++ b/src/ImageSharp/Formats/Png/PngTextData.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
diff --git a/src/ImageSharp/Formats/Png/PngThrowHelper.cs b/src/ImageSharp/Formats/Png/PngThrowHelper.cs
index 8700438bd..7cf954f1d 100644
--- a/src/ImageSharp/Formats/Png/PngThrowHelper.cs
+++ b/src/ImageSharp/Formats/Png/PngThrowHelper.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Runtime.CompilerServices;
@@ -21,6 +21,9 @@ namespace SixLabors.ImageSharp.Formats.Png
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNoData() => throw new InvalidImageContentException("PNG Image does not contain a data chunk");
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowMissingPalette() => throw new InvalidImageContentException("PNG Image does not contain a palette chunk");
+
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidChunkType() => throw new InvalidImageContentException("Invalid PNG data.");
diff --git a/src/ImageSharp/Formats/Png/PngTransparentColorMode.cs b/src/ImageSharp/Formats/Png/PngTransparentColorMode.cs
index fe92e7fbf..4505787cc 100644
--- a/src/ImageSharp/Formats/Png/PngTransparentColorMode.cs
+++ b/src/ImageSharp/Formats/Png/PngTransparentColorMode.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Png
{
diff --git a/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs b/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs
index 240b8b9b3..d2d0944a2 100644
--- a/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs
+++ b/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Tga
{
diff --git a/src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs b/src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs
index 21599902a..a6fd215db 100644
--- a/src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Tga
{
diff --git a/src/ImageSharp/Formats/Tga/MetadataExtensions.cs b/src/ImageSharp/Formats/Tga/MetadataExtensions.cs
index 00bdb0188..2f085af73 100644
--- a/src/ImageSharp/Formats/Tga/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Tga/MetadataExtensions.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Metadata;
diff --git a/src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs b/src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs
index 7c04ed4d8..1e5083ce5 100644
--- a/src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs
+++ b/src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Tga
{
diff --git a/src/ImageSharp/Formats/Tga/TgaCompression.cs b/src/ImageSharp/Formats/Tga/TgaCompression.cs
index 526d3b3e5..fc9601a48 100644
--- a/src/ImageSharp/Formats/Tga/TgaCompression.cs
+++ b/src/ImageSharp/Formats/Tga/TgaCompression.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Tga
{
diff --git a/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs b/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs
index eecd5f0ea..6ea273fb4 100644
--- a/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs
+++ b/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Tga
{
diff --git a/src/ImageSharp/Formats/Tga/TgaConstants.cs b/src/ImageSharp/Formats/Tga/TgaConstants.cs
index 990c2bcb1..2dd8c3e80 100644
--- a/src/ImageSharp/Formats/Tga/TgaConstants.cs
+++ b/src/ImageSharp/Formats/Tga/TgaConstants.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.Collections.Generic;
diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs
index e06a0ee88..a256850e6 100644
--- a/src/ImageSharp/Formats/Tga/TgaDecoder.cs
+++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs
@@ -1,11 +1,8 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
-using SixLabors.ImageSharp.IO;
-using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tga
@@ -16,48 +13,25 @@ namespace SixLabors.ImageSharp.Formats.Tga
public sealed class TgaDecoder : IImageDecoder, ITgaDecoderOptions, IImageInfoDetector
{
///
- public Image Decode(Configuration configuration, Stream stream)
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(stream, nameof(stream));
var decoder = new TgaDecoderCore(configuration, this);
- return decoder.Decode(configuration, stream);
+ return decoder.Decode(configuration, stream, cancellationToken);
}
///
- public Image Decode(Configuration configuration, Stream stream)
- => this.Decode(configuration, stream);
+ public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
+ => this.Decode(configuration, stream, cancellationToken);
///
- public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- where TPixel : unmanaged, IPixel
- {
- Guard.NotNull(stream, nameof(stream));
-
- var decoder = new TgaDecoderCore(configuration, this);
- return decoder.DecodeAsync(configuration, stream, cancellationToken);
- }
-
- ///
- public async Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
- => await this.DecodeAsync(configuration, stream, cancellationToken)
- .ConfigureAwait(false);
-
- ///
- public IImageInfo Identify(Configuration configuration, Stream stream)
- {
- Guard.NotNull(stream, nameof(stream));
-
- return new TgaDecoderCore(configuration, this).Identify(configuration, stream);
- }
-
- ///
- public Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken)
+ public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(stream, nameof(stream));
- return new TgaDecoderCore(configuration, this).IdentifyAsync(configuration, stream, cancellationToken);
+ return new TgaDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken);
}
}
}
diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
index d101ccd94..af586e254 100644
--- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
@@ -1,5 +1,5 @@
// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
+// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
@@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
///
/// Gets the dimensions of the image.
///
- public Size Dimensions => new Size(this.fileHeader.Width, this.fileHeader.Height);
+ public Size Dimensions => new(this.fileHeader.Width, this.fileHeader.Height);
///
public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken)
@@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
this.currentStream.Skip(this.fileHeader.IdLength);
// Parse the color map, if present.
- if (this.fileHeader.ColorMapType != 0 && this.fileHeader.ColorMapType != 1)
+ if (this.fileHeader.ColorMapType is not 0 and not 1)
{
TgaThrowHelper.ThrowNotSupportedException($"Unknown tga colormap type {this.fileHeader.ColorMapType} found");
}
@@ -117,7 +117,11 @@ namespace SixLabors.ImageSharp.Formats.Tga
using (IMemoryOwner palette = this.memoryAllocator.Allocate(colorMapSizeInBytes, AllocationOptions.Clean))
{
Span paletteSpan = palette.GetSpan();
- this.currentStream.Read(paletteSpan, this.fileHeader.CMapStart, colorMapSizeInBytes);
+ int bytesRead = this.currentStream.Read(paletteSpan, this.fileHeader.CMapStart, colorMapSizeInBytes);
+ if (bytesRead != colorMapSizeInBytes)
+ {
+ TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read the color map");
+ }
if (this.fileHeader.ImageType == TgaImageType.RleColorMapped)
{
@@ -308,8 +312,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
private void ReadPalettedRle(int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel
{
- int bytesPerPixel = 1;
- using (IMemoryOwner buffer = this.memoryAllocator.Allocate